pantsdown 1.4.3 → 1.4.5

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 CHANGED
@@ -132,8 +132,6 @@ console.log(html);
132
132
 
133
133
  ## Roadmap
134
134
 
135
- - [x] Add id & anchors to headings
136
- - [ ] Add sourcemap to list items
137
135
  - [ ] Add button to copy code in fences
138
136
  - [ ] [Mermaid](https://mermaid.js.org/) support
139
137
  - [ ] [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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pantsdown",
3
- "version": "1.4.3",
3
+ "version": "1.4.5",
4
4
  "author": "wallpants",
5
5
  "description": "Markdown to 'GitHub HTML' parser",
6
6
  "license": "MIT",
package/src/parser.ts CHANGED
@@ -126,13 +126,13 @@ export class Parser {
126
126
  itemBody,
127
127
  task,
128
128
  Boolean(checked),
129
- token.sourceMap![0] + j,
129
+ item.sourceMap,
130
130
  );
131
131
  }
132
132
 
133
133
  const listClasses: string[] = [];
134
134
  if (containsTaskList) listClasses.push("contains-task-list");
135
- out += this.renderer.list(body, ordered, start, token.sourceMap, listClasses);
135
+ out += this.renderer.list(body, ordered, start, listClasses);
136
136
  continue;
137
137
  }
138
138
  case "html": {
package/src/renderer.ts CHANGED
@@ -10,7 +10,6 @@ import {
10
10
  getHtmlElementText,
11
11
  injectHtmlAttributes,
12
12
  renderHtmlClasses,
13
- renderSourceMap,
14
13
  } from "./utils.ts";
15
14
 
16
15
  const defaultConfig: NonNullable<PantsdownConfig["renderer"]> = {
@@ -38,13 +37,14 @@ export class Renderer {
38
37
  const language = lang && hljs.getLanguage(lang) ? lang : "plaintext";
39
38
  const highlightedCode = hljs.highlight(code, { language }).value;
40
39
 
41
- return (
42
- `<pre${renderSourceMap(sourceMap)}><code class="hljs language-` +
40
+ const result =
41
+ `<pre><code class="hljs language-` +
43
42
  escape(language) +
44
43
  '">' +
45
44
  highlightedCode +
46
- "</code></pre>\n"
47
- );
45
+ "</code></pre>\n";
46
+
47
+ return injectHtmlAttributes(result, [], sourceMap);
48
48
  }
49
49
 
50
50
  blockquote(quote: string): string {
@@ -54,64 +54,45 @@ export class Renderer {
54
54
  html(html: string, _block: boolean, sourceMap?: SourceMap | undefined): string {
55
55
  const result = fixHtmlLocalImageHref(html, this.rendererConfig.relativeImageUrlPrefix);
56
56
 
57
- const htmlAttributes: [name: string, value: string | number][] = [];
58
-
59
- if (sourceMap) {
60
- htmlAttributes.push(["line-start", sourceMap[0]], ["line-end", sourceMap[1]]);
61
- }
57
+ const attrs: [name: string, value: string | number][] = [];
62
58
 
63
59
  if (this.rendererConfig.detailsTagDefaultOpen) {
64
60
  const tag = inline.tag.exec(html);
65
61
  if (tag?.[0] === "<details>") {
66
- htmlAttributes.push(["open", ""]);
62
+ attrs.push(["open", ""]);
67
63
  }
68
64
  }
69
65
 
70
- return injectHtmlAttributes(result, htmlAttributes);
66
+ return injectHtmlAttributes(result, attrs, sourceMap);
71
67
  }
72
68
 
73
69
  heading(text: string, level: number, sourceMap: SourceMap): string {
74
70
  const elementText = getHtmlElementText(text);
75
71
  const slug = this.slugger.slug(elementText);
76
- let result = `<h${level}${renderSourceMap(sourceMap)} style="position: relative;">`;
72
+ let result = `<h${level} style="position: relative;">`;
77
73
  // span with negative top to add some offset when scrolling to #slug
78
74
  result += `<span style="position: absolute; top: -50px;" id="${slug}"></span>`;
79
75
  result += `${text}<a class="anchor octicon-link" href="#${slug}"></a>`;
80
76
  result += `</h${level}>\n`;
81
- return result;
77
+ return injectHtmlAttributes(result, [], sourceMap);
82
78
  }
83
79
 
84
80
  hr(sourceMap: SourceMap): string {
85
- return `<hr${renderSourceMap(sourceMap)}>\n`;
81
+ return injectHtmlAttributes(`<hr>\n`, [], sourceMap);
86
82
  }
87
83
 
88
- list(
89
- body: string,
90
- ordered: boolean,
91
- start: number | "",
92
- sourceMap: SourceMap,
93
- classes: string[] = [],
94
- ): string {
84
+ list(body: string, ordered: boolean, start: number | "", classes: string[] = []): string {
95
85
  const type = ordered ? "ol" : "ul";
96
86
  const startatt = ordered && start !== 1 ? ' start="' + start + '"' : "";
97
87
  return (
98
- "<" +
99
- type +
100
- startatt +
101
- `${renderSourceMap(sourceMap)}${renderHtmlClasses(classes)}>\n` +
102
- body +
103
- "</" +
104
- type +
105
- ">\n"
88
+ "<" + type + startatt + `${renderHtmlClasses(classes)}>\n` + body + "</" + type + ">\n"
106
89
  );
107
90
  }
108
91
 
109
- listitem(text: string, task: boolean, _checked: boolean, lineStart: number): string {
110
- const classes: string[] = [];
111
- if (task) classes.push("task-list-item");
112
- return `<li${renderHtmlClasses(
113
- classes,
114
- )} line-start="${lineStart}" line-end="${lineStart}">${text}</li>\n`;
92
+ listitem(text: string, task: boolean, _checked: boolean, sourceMap: SourceMap): string {
93
+ const attrs: [string, string][] = [];
94
+ if (task) attrs.push(["class", "task-list-item"]);
95
+ return injectHtmlAttributes(`<li>${text}</li>\n`, attrs, sourceMap);
115
96
  }
116
97
 
117
98
  checkbox(checked: boolean, classes: string[] = []): string {
@@ -123,7 +104,7 @@ export class Renderer {
123
104
  }
124
105
 
125
106
  paragraph(text: string, sourceMap: SourceMap): string {
126
- return `<p${renderSourceMap(sourceMap)}>${text}</p>\n`;
107
+ return injectHtmlAttributes(`<p>${text}</p>\n`, [], sourceMap);
127
108
  }
128
109
 
129
110
  table(header: string, body: string): string {
@@ -132,9 +113,11 @@ export class Renderer {
132
113
  }
133
114
 
134
115
  tablerow(content: string, sourceMapStart: number | undefined): string {
135
- return `<tr${renderSourceMap(
116
+ return injectHtmlAttributes(
117
+ `<tr>\n${content}</tr>\n`,
118
+ [],
136
119
  sourceMapStart ? [sourceMapStart, sourceMapStart] : undefined,
137
- )}>\n${content}</tr>\n`;
120
+ );
138
121
  }
139
122
 
140
123
  tablecell(
package/src/tokenizer.ts CHANGED
@@ -132,7 +132,6 @@ export class Tokenizer {
132
132
  start: isordered ? +bull.slice(0, -1) : "",
133
133
  loose: false,
134
134
  items: [] as Tokens["ListItem"][],
135
- sourceMap: [0, 0], // updated below once token.raw is built
136
135
  };
137
136
 
138
137
  bull = isordered ? `\\d{1,9}\\${bull.slice(-1)}` : `\\${bull}`;
@@ -294,10 +293,9 @@ export class Tokenizer {
294
293
  list.items[list.items.length - 1]!.text = itemContents.trimEnd();
295
294
  list.raw = list.raw.trimEnd();
296
295
 
297
- list.sourceMap = this.lexer.getSourceMap(list.raw);
298
-
299
296
  // Item child tokens handled here at end because we needed to have the final item to trim it first
300
297
  for (let i = 0, listItemsLen = list.items.length; i < listItemsLen; i++) {
298
+ list.items[i]!.sourceMap = this.lexer.getSourceMap(list.items[i]!.raw);
301
299
  this.lexer.state.top = false;
302
300
  list.items[i]!.tokens = this.lexer.blockTokens(list.items[i]!.text, []);
303
301
 
package/src/types.ts CHANGED
@@ -61,7 +61,6 @@ export interface Tokens extends Record<string, BaseToken> {
61
61
  start: number | "";
62
62
  loose: boolean;
63
63
  items: Tokens["ListItem"][];
64
- sourceMap: SourceMap;
65
64
  };
66
65
  ListItem: {
67
66
  type: "list_item";
@@ -71,6 +70,7 @@ export interface Tokens extends Record<string, BaseToken> {
71
70
  checked?: boolean | undefined;
72
71
  loose: boolean;
73
72
  tokens: Token[];
73
+ sourceMap?: SourceMap;
74
74
  };
75
75
  Paragraph: {
76
76
  type: "paragraph";
package/src/utils.ts CHANGED
@@ -41,20 +41,15 @@ export function renderHtmlClasses(classes: string[]) {
41
41
  return result;
42
42
  }
43
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
44
  export function getHtmlElementText(html: string) {
56
45
  try {
57
- const element = parseHtmlElement(html);
46
+ const parser = new DOMParser();
47
+ const doc = parser.parseFromString(html, "text/html");
48
+ // eslint-disable-next-line
49
+ if (!doc.body) throw Error("Invalid HTML");
50
+ const element = doc.body.firstChild as HTMLElement;
51
+ // eslint-disable-next-line
52
+ if (!element) throw Error("No valid element found");
58
53
  return element.textContent ?? html;
59
54
  } catch (_) {
60
55
  return html;
@@ -70,14 +65,20 @@ export function getHtmlElementText(html: string) {
70
65
  export function injectHtmlAttributes(
71
66
  html: string,
72
67
  attrs: [name: string, value: string | number][],
68
+ sourceMap: SourceMap,
73
69
  ) {
70
+ if (sourceMap) {
71
+ attrs.push(["line-start", sourceMap[0]]);
72
+ attrs.push(["line-end", sourceMap[1]]);
73
+ }
74
+
74
75
  if (!attrs.length) return html;
75
76
 
76
- const closingBracket = /[a-zA-Z0-9]>|\/>/;
77
+ const closingBracket = /[a-zA-Z0-9\/"]>/;
77
78
  const match = html.match(closingBracket);
78
79
  if (match) {
79
80
  let htmlAttrs = "";
80
- attrs.forEach((atrr) => (htmlAttrs += ` ${atrr[0]}=${atrr[1]}`));
81
+ attrs.forEach((atrr) => (htmlAttrs += ` ${atrr[0]}="${atrr[1]}"`));
81
82
  const sliceIdx = match.index! + (match[0] === "/>" ? -1 : 1);
82
83
  return html.slice(0, sliceIdx) + htmlAttrs + html.slice(sliceIdx);
83
84
  }
@@ -128,11 +129,6 @@ export function fixLocalImageHref(
128
129
  }
129
130
  }
130
131
 
131
- export function renderSourceMap(sourceMap: SourceMap) {
132
- if (!sourceMap) return "";
133
- return ` line-start=${sourceMap[0]} line-end=${sourceMap[1]}`;
134
- }
135
-
136
132
  export function cleanUrl(href: string) {
137
133
  try {
138
134
  href = encodeURI(href).replace(/%25/g, "%");