pantsdown 1.3.1 → 1.4.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.
@@ -1,4 +1,4 @@
1
1
  // TODO: I'm not so sure about this, come back to this
2
2
  // once Bun has official css loader
3
- const path: string;
4
- export default path;
3
+ const styles: string;
4
+ export default styles;
package/src/pantsdown.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import GithubSlugger from "github-slugger";
1
2
  import { Lexer } from "./lexer.ts";
2
3
  import { Parser } from "./parser.ts";
3
4
  import { type PantsdownConfig } from "./types.ts";
@@ -12,6 +13,10 @@ export class Pantsdown {
12
13
  }
13
14
 
14
15
  parse(src: string): string {
16
+ // re-init slugger to avoid slug count from incorrectly incrementing
17
+ // from previosly slugged headings
18
+ this.parser.renderer.slugger = new GithubSlugger();
19
+
15
20
  const tokens = this.lexer.lex(src);
16
21
  const html = this.parser.parse(tokens);
17
22
  return html;
package/src/parser.ts CHANGED
@@ -5,7 +5,7 @@ import { type PantsdownConfig, type Token, type Tokens } from "./types.ts";
5
5
  * Parsing & Compiling
6
6
  */
7
7
  export class Parser {
8
- private renderer: Renderer;
8
+ renderer: Renderer;
9
9
 
10
10
  constructor(config: PantsdownConfig | undefined) {
11
11
  this.renderer = new Renderer(config);
@@ -83,6 +83,7 @@ export class Parser {
83
83
  const ordered = token.ordered;
84
84
  const start = token.start;
85
85
  const loose = token.loose;
86
+ let containsTaskList = false;
86
87
 
87
88
  let body = "";
88
89
  for (let j = 0, itemsLen = token.items.length; j < itemsLen; j++) {
@@ -92,7 +93,10 @@ export class Parser {
92
93
 
93
94
  let itemBody = "";
94
95
  if (item.task) {
95
- const checkbox = this.renderer.checkbox(Boolean(checked));
96
+ containsTaskList = true;
97
+ const checkbox = this.renderer.checkbox(Boolean(checked), [
98
+ "task-list-item-checkbox",
99
+ ]);
96
100
  if (loose) {
97
101
  if (
98
102
  item.tokens.length > 0 &&
@@ -121,7 +125,9 @@ export class Parser {
121
125
  body += this.renderer.listitem(itemBody, task, Boolean(checked));
122
126
  }
123
127
 
124
- out += this.renderer.list(body, ordered, start, token.sourceMap);
128
+ const listClasses: string[] = [];
129
+ if (containsTaskList) listClasses.push("contains-task-list");
130
+ out += this.renderer.list(body, ordered, start, token.sourceMap, listClasses);
125
131
  continue;
126
132
  }
127
133
  case "html": {
package/src/renderer.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import GithubSlugger from "github-slugger";
1
2
  import hljs from "highlight.js";
2
3
  import { type PantsdownConfig, type SourceMap } from "./types.ts";
3
4
  import {
@@ -5,7 +6,9 @@ import {
5
6
  escape,
6
7
  fixHtmlLocalImageHref,
7
8
  fixLocalImageHref,
9
+ getHtmlElementText,
8
10
  injectHtmlAttributes,
11
+ renderHtmlClasses,
9
12
  renderSourceMap,
10
13
  } from "./utils.ts";
11
14
 
@@ -18,6 +21,7 @@ const defaultConfig: NonNullable<PantsdownConfig["renderer"]> = {
18
21
  */
19
22
  export class Renderer {
20
23
  private rendererConfig: NonNullable<PantsdownConfig["renderer"]>;
24
+ slugger = new GithubSlugger();
21
25
 
22
26
  constructor(config: PantsdownConfig | undefined) {
23
27
  const rendererConfig = Object.assign(defaultConfig, config?.renderer ?? {});
@@ -57,28 +61,53 @@ export class Renderer {
57
61
  }
58
62
 
59
63
  heading(text: string, level: number, sourceMap: SourceMap): string {
60
- // ignore IDs
61
- return `<h${level}${renderSourceMap(sourceMap)}>${text}</h${level}>\n`;
64
+ const elementText = getHtmlElementText(text);
65
+ const slug = this.slugger.slug(elementText);
66
+ let result = `<h${level}${renderSourceMap(sourceMap)} style="position: relative;">`;
67
+ // span with negative top to add some offset when scrolling to #slug
68
+ result += `<span style="position: absolute; top: -50px;" id="${slug}"></span>`;
69
+ result += `${text}<a class="anchor octicon-link" href="#${slug}"></a>`;
70
+ result += `</h${level}>\n`;
71
+ return result;
62
72
  }
63
73
 
64
74
  hr(sourceMap: SourceMap): string {
65
75
  return `<hr${renderSourceMap(sourceMap)}>\n`;
66
76
  }
67
77
 
68
- list(body: string, ordered: boolean, start: number | "", sourceMap: SourceMap): string {
78
+ list(
79
+ body: string,
80
+ ordered: boolean,
81
+ start: number | "",
82
+ sourceMap: SourceMap,
83
+ classes: string[] = [],
84
+ ): string {
69
85
  const type = ordered ? "ol" : "ul";
70
86
  const startatt = ordered && start !== 1 ? ' start="' + start + '"' : "";
71
87
  return (
72
- "<" + type + startatt + `${renderSourceMap(sourceMap)}>\n` + body + "</" + type + ">\n"
88
+ "<" +
89
+ type +
90
+ startatt +
91
+ `${renderSourceMap(sourceMap)}${renderHtmlClasses(classes)}>\n` +
92
+ body +
93
+ "</" +
94
+ type +
95
+ ">\n"
73
96
  );
74
97
  }
75
98
 
76
- listitem(text: string, _task: boolean, _checked: boolean): string {
77
- return `<li>${text}</li>\n`;
99
+ listitem(text: string, task: boolean, _checked: boolean): string {
100
+ const classes: string[] = [];
101
+ if (task) classes.push("task-list-item");
102
+ return `<li${renderHtmlClasses(classes)}>${text}</li>\n`;
78
103
  }
79
104
 
80
- checkbox(checked: boolean): string {
81
- return "<input " + (checked ? 'checked="" ' : "") + 'disabled="" type="checkbox">';
105
+ checkbox(checked: boolean, classes: string[] = []): string {
106
+ return (
107
+ "<input " +
108
+ (checked ? 'checked="" ' : "") +
109
+ `disabled="" type="checkbox"${renderHtmlClasses(classes)}>`
110
+ );
82
111
  }
83
112
 
84
113
  paragraph(text: string, sourceMap: SourceMap): string {
package/src/utils.ts CHANGED
@@ -33,18 +33,40 @@ 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
+ function parseHtmlElement(html: string) {
45
+ const parser = new DOMParser();
46
+ const doc = parser.parseFromString(html, "text/html");
47
+ // eslint-disable-next-line
48
+ if (!doc.body) throw Error("Invalid HTML");
49
+ const element = doc.body.firstChild as HTMLElement;
50
+ // eslint-disable-next-line
51
+ if (!element) throw Error("No valid element found");
52
+ return element;
53
+ }
54
+
55
+ export function getHtmlElementText(html: string) {
56
+ try {
57
+ const element = parseHtmlElement(html);
58
+ return element.textContent ?? html;
59
+ } catch (_) {
60
+ return html;
61
+ }
62
+ }
63
+
36
64
  export function injectHtmlAttributes(
37
65
  html: string,
38
66
  attrs: [name: string, value: string | number][],
39
67
  ) {
40
- const parser = new DOMParser();
41
68
  try {
42
- const doc = parser.parseFromString(html, "text/html");
43
- // eslint-disable-next-line
44
- if (!doc.body) throw Error("Invalid HTML");
45
- const element = doc.body.firstChild as HTMLElement;
46
- // eslint-disable-next-line
47
- if (!element) throw Error("No valid element found");
69
+ const element = parseHtmlElement(html);
48
70
  attrs.forEach(([name, value]) => {
49
71
  element.setAttribute(name, JSON.stringify(value));
50
72
  });
package/LICENSE.md DELETED
@@ -1,67 +0,0 @@
1
- # License information
2
-
3
- ## Contribution License Agreement
4
-
5
- If you contribute code to this project, you are implicitly allowing your code
6
- to be distributed under the MIT license. You are also implicitly verifying that
7
- all code is your original work.
8
-
9
- ## Pantsdown
10
-
11
- Copyright (c) 2023+, Wallpants (https://github.com/wallpants/)
12
-
13
- Permission is hereby granted, free of charge, to any person obtaining a copy
14
- of this software and associated documentation files (the "Software"), to deal
15
- in the Software without restriction, including without limitation the rights
16
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
- copies of the Software, and to permit persons to whom the Software is
18
- furnished to do so, subject to the following conditions:
19
-
20
- The above copyright notice and this permission notice shall be included in
21
- all copies or substantial portions of the Software.
22
-
23
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29
- THE SOFTWARE.
30
-
31
- ## Marked
32
-
33
- Copyright (c) 2018+, MarkedJS (https://github.com/markedjs/)
34
-
35
- Copyright (c) 2011-2018, Christopher Jeffrey (https://github.com/chjj/)
36
-
37
- Permission is hereby granted, free of charge, to any person obtaining a copy
38
- of this software and associated documentation files (the "Software"), to deal
39
- in the Software without restriction, including without limitation the rights
40
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
41
- copies of the Software, and to permit persons to whom the Software is
42
- furnished to do so, subject to the following conditions:
43
-
44
- The above copyright notice and this permission notice shall be included in
45
- all copies or substantial portions of the Software.
46
-
47
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
48
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
49
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
50
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
51
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
52
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
53
- THE SOFTWARE.
54
-
55
- ## Markdown
56
-
57
- Copyright © 2004, John Gruber
58
- http://daringfireball.net/
59
- All rights reserved.
60
-
61
- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
62
-
63
- - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
64
- - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
65
- - Neither the name “Markdown” nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
66
-
67
- This software is provided by the copyright holders and contributors “as is” and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright owner or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage.