pantsdown 2.2.6 → 2.2.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pantsdown",
3
- "version": "2.2.6",
3
+ "version": "2.2.7",
4
4
  "description": "Markdown to 'GitHub HTML' parser",
5
5
  "license": "MIT",
6
6
  "author": "wallpants",
@@ -8,6 +8,10 @@
8
8
  "type": "git",
9
9
  "url": "git://github.com/wallpants/pantsdown.git"
10
10
  },
11
+ "files": [
12
+ "src",
13
+ "!src/**/*.test.ts"
14
+ ],
11
15
  "type": "module",
12
16
  "exports": {
13
17
  "./*": "./*",
@@ -26,33 +30,29 @@
26
30
  "commit": "cz",
27
31
  "format": "oxfmt .",
28
32
  "typecheck": "tsc",
29
- "lint": "eslint .",
33
+ "lint": "oxlint .",
30
34
  "check": "bun run typecheck && bun run lint",
31
35
  "docs:build": "bun run docs/build.ts"
32
36
  },
33
37
  "dependencies": {
34
38
  "github-slugger": "^2.0.0",
35
39
  "highlight.js": "^11.11.1",
36
- "katex": "^0.17.0"
40
+ "katex": "^0.18.0"
37
41
  },
38
42
  "devDependencies": {
39
- "@commitlint/config-conventional": "^21.0.2",
40
- "@commitlint/cz-commitlint": "^21.0.2",
41
- "@happy-dom/global-registrator": "^20.9.0",
43
+ "@commitlint/config-conventional": "^21.2.0",
44
+ "@commitlint/cz-commitlint": "^21.2.0",
45
+ "@happy-dom/global-registrator": "^20.11.0",
42
46
  "@types/bun": "^1.3.14",
43
47
  "@types/katex": "^0.16.8",
44
- "@typescript-eslint/eslint-plugin": "^8.60.0",
45
- "@typescript-eslint/parser": "^8.60.0",
46
- "commitizen": "^4.3.1",
47
- "commitlint": "^21.0.2",
48
- "eslint": "^9.39.4",
49
- "eslint-import-resolver-typescript": "^4.4.4",
50
- "eslint-plugin-import-x": "^4.16.2",
48
+ "commitizen": "^4.3.2",
49
+ "commitlint": "^21.2.1",
51
50
  "inquirer": "^12.11.1",
52
- "oxfmt": "^0.52.0",
53
- "semantic-release": "^25.0.3",
54
- "typescript": "6.0.3",
55
- "typescript-eslint": "^8.60.0"
51
+ "oxfmt": "^0.59.0",
52
+ "oxlint": "^1.73.0",
53
+ "oxlint-tsgolint": "^0.25.0",
54
+ "semantic-release": "^25.0.8",
55
+ "typescript": "7.0.2"
56
56
  },
57
57
  "commitlint": {
58
58
  "extends": [
package/src/lexer.ts CHANGED
@@ -31,6 +31,11 @@ export class Lexer {
31
31
  this.tokens = [];
32
32
  this.links = {};
33
33
  this.line = 1;
34
+ this.state = {
35
+ inLink: false,
36
+ inRawBlock: false,
37
+ top: true,
38
+ };
34
39
 
35
40
  src = src.replace(/\r\n|\r/g, "\n");
36
41
 
package/src/renderer.ts CHANGED
@@ -33,7 +33,9 @@ export class Renderer {
33
33
 
34
34
  if (lang === "mermaid") {
35
35
  attrs.push(["class", "mermaid-container mermaid"]);
36
- const result = `<pre style="position: relative;">` + code + "\n" + `</pre>`;
36
+ // escape so diagram source can't be parsed as HTML;
37
+ // mermaid reads textContent, which undoes the escaping
38
+ const result = `<pre style="position: relative;">` + escape(code, true) + "\n" + `</pre>`;
37
39
  return injectHtmlAttributes(result, attrs, sourceMap);
38
40
  }
39
41
 
package/src/tokenizer.ts CHANGED
@@ -317,7 +317,8 @@ export class Tokenizer {
317
317
  const lastTrimmed = raw.trimEnd();
318
318
 
319
319
  if (list.items[list.items.length - 1]!.sourceMap) {
320
- this.lexer.line -= raw.length - lastTrimmed.length;
320
+ // give back the trimmed trailing newlines; the counter tracks lines, not chars
321
+ this.lexer.line -= (raw.slice(lastTrimmed.length).match(/\n/g) ?? []).length;
321
322
  }
322
323
 
323
324
  list.items[list.items.length - 1]!.raw = lastTrimmed;
@@ -333,8 +334,7 @@ export class Tokenizer {
333
334
  // Check if list should be loose
334
335
  const spacers = list.items[i]!.tokens.filter((t) => t.type === "space");
335
336
  const hasMultipleLineBreaks =
336
- // eslint-disable-next-line
337
- spacers.length > 0 && spacers.some((t: any) => /\n.*\n/.test(t.raw));
337
+ spacers.length > 0 && spacers.some((t) => /\n.*\n/.test(t.raw));
338
338
 
339
339
  list.loose = hasMultipleLineBreaks;
340
340
  }
@@ -703,13 +703,11 @@ export class Tokenizer {
703
703
  // _ can't be between two alphanumerics. \p{L}\p{N} includes non-english alphabet/numbers as well
704
704
  if (match[3] && /[\p{L}\p{N}]/u.exec(prevChar)) return;
705
705
 
706
- // eslint-disable-next-line
707
706
  const nextChar = match[1] || match[2] || "";
708
707
 
709
708
  if (!nextChar || !prevChar || inline.punctuation.exec(prevChar)) {
710
- // unicode Regex counts emoji as 1 char; spread into array for proper count (used multiple times below)
711
- // eslint-disable-next-line @typescript-eslint/no-misused-spread
712
- const lLength = [...match[0]].length - 1;
709
+ // unicode Regex counts emoji as 1 char; convert to array for proper count (used multiple times below)
710
+ const lLength = Array.from(match[0]).length - 1;
713
711
  let rDelim,
714
712
  rLength,
715
713
  delimTotal = lLength,
@@ -724,13 +722,11 @@ export class Tokenizer {
724
722
  maskedSrc = maskedSrc.slice(-1 * src.length + lLength);
725
723
 
726
724
  while ((match = endReg.exec(maskedSrc)) != null) {
727
- // eslint-disable-next-line
728
725
  rDelim = match[1] || match[2] || match[3] || match[4] || match[5] || match[6];
729
726
 
730
727
  if (!rDelim) continue; // skip single * in __abc*abc__
731
728
 
732
- // eslint-disable-next-line @typescript-eslint/no-misused-spread
733
- rLength = [...rDelim].length;
729
+ rLength = Array.from(rDelim).length;
734
730
 
735
731
  if (match[3] || match[4]) {
736
732
  // found another Left Delim
@@ -751,8 +747,7 @@ export class Tokenizer {
751
747
  // Remove extra characters. *a*** -> *a*
752
748
  rLength = Math.min(rLength, rLength + delimTotal + midDelimTotal);
753
749
  // char length can be >1 for unicode characters;
754
- // eslint-disable-next-line @typescript-eslint/no-misused-spread
755
- const lastCharLength = [...match[0]][0]!.length;
750
+ const lastCharLength = Array.from(match[0])[0]!.length;
756
751
  const raw = src.slice(0, lLength + match.index + lastCharLength + rLength);
757
752
 
758
753
  // Create `em` if smallest delimiter has odd char count. *a***
package/src/types.ts CHANGED
@@ -9,7 +9,7 @@ type BaseToken = {
9
9
 
10
10
  export type SourceMap = [start: number, end: number] | undefined;
11
11
 
12
- // eslint-disable-next-line @typescript-eslint/consistent-type-definitions
12
+ // eslint-disable-next-line typescript/consistent-type-definitions
13
13
  export interface Tokens extends Record<string, BaseToken> {
14
14
  Space: {
15
15
  type: "space";
package/src/utils.ts CHANGED
@@ -37,10 +37,7 @@ export function getHtmlElementText(html: string) {
37
37
  try {
38
38
  const parser = new DOMParser();
39
39
  const doc = parser.parseFromString(html, "text/html");
40
- // eslint-disable-next-line
41
- if (!doc.body) throw Error("Invalid HTML");
42
- const element = doc.body.firstChild as HTMLElement;
43
- // eslint-disable-next-line
40
+ const element = doc.body.firstChild;
44
41
  if (!element) throw Error("No valid element found");
45
42
  return element.textContent || html;
46
43
  } catch (_) {
@@ -56,12 +53,15 @@ export function injectHtmlAttributes(html: string, attrs: HTMLAttrs, sourceMap?:
56
53
 
57
54
  if (!attrs.length) return html;
58
55
 
56
+ // closing tags cannot carry attributes
57
+ if (/^\s*<\//.test(html)) return html;
58
+
59
59
  const closingBracket = /[a-zA-Z0-9\/"]>/;
60
60
  const match = closingBracket.exec(html);
61
61
  if (match) {
62
62
  let htmlAttrs = "";
63
63
  attrs.forEach((atrr) => (htmlAttrs += ` ${atrr[0]}="${atrr[1]}"`));
64
- const sliceIdx = match.index + (match[0] === "/>" ? -1 : 1);
64
+ const sliceIdx = match.index + (match[0] === "/>" ? 0 : 1);
65
65
  return html.slice(0, sliceIdx) + htmlAttrs + html.slice(sliceIdx);
66
66
  }
67
67
  return html;
@@ -314,7 +314,7 @@ export function indentCodeCompensation(raw: string, text: string) {
314
314
  }
315
315
 
316
316
  function makeAlertRegex(type: string) {
317
- return new RegExp(`^(?:\\[\\!${type.toUpperCase()}\\]|[\\*]{2}${type}[\\*]{2})[\s]*?\n?`);
317
+ return new RegExp(`^(?:\\[\\!${type.toUpperCase()}\\]|[\\*]{2}${type}[\\*]{2})[ \\t]*\\n?`);
318
318
  }
319
319
 
320
320
  export const ALERTS = [
package/.oxfmtrc.json DELETED
@@ -1,17 +0,0 @@
1
- {
2
- "$schema": "./node_modules/oxfmt/configuration_schema.json",
3
- "ignorePatterns": [".github"],
4
- "printWidth": 100,
5
- "tabWidth": 3,
6
- "sortImports": {
7
- "newlinesBetween": false
8
- },
9
- "overrides": [
10
- {
11
- "files": ["*.json", "*.md"],
12
- "options": {
13
- "tabWidth": 2
14
- }
15
- }
16
- ]
17
- }
package/CLAUDE.md DELETED
@@ -1,73 +0,0 @@
1
- # CLAUDE.md
2
-
3
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
-
5
- ## Project Overview
6
-
7
- Pantsdown is a Markdown to HTML converter that renders markdown similar to GitHub's styling. It was built specifically for [github-preview.nvim](https://github.com/wallpants/github-preview.nvim). Based on [Marked](https://github.com/markedjs/marked).
8
-
9
- ## Commands
10
-
11
- ```bash
12
- # Type checking
13
- bun run typecheck
14
-
15
- # Linting
16
- bun run lint
17
-
18
- # Both typecheck and lint
19
- bun run check
20
-
21
- # Run tests (uses bun:test with happy-dom)
22
- bun test
23
-
24
- # Run a single test file
25
- bun test tests/parse.test.ts
26
-
27
- # Update test snapshots
28
- bun test --update-snapshots
29
-
30
- # Format code
31
- bun run format
32
-
33
- # Build docs
34
- bun run docs:build
35
- ```
36
-
37
- ## Architecture
38
-
39
- The parsing pipeline follows a classic compiler pattern:
40
-
41
- 1. **Lexer** (`src/lexer.ts`) - Tokenizes markdown source into an array of tokens
42
- - Uses `Tokenizer` for the actual token creation
43
- - Processes block-level tokens first, then inline tokens
44
- - Tracks source maps for line number references
45
- - Collects footnotes separately
46
-
47
- 2. **Tokenizer** (`src/tokenizer.ts`) - Creates tokens from markdown patterns
48
- - Uses regex rules from `src/rules/block.ts` and `src/rules/inline.ts`
49
-
50
- 3. **Parser** (`src/parser.ts`) - Converts tokens to HTML by dispatching to the Renderer
51
- - Recursively processes nested tokens
52
-
53
- 4. **Renderer** (`src/renderer.ts`) - Produces HTML output for each token type
54
- - Uses highlight.js for syntax highlighting
55
- - Uses github-slugger for heading anchors
56
- - Handles special cases like mermaid diagrams and alerts
57
-
58
- Entry point: `Pantsdown` class in `src/pantsdown.ts` coordinates the pipeline.
59
-
60
- ## Key Types
61
-
62
- All token types are defined in `src/types.ts`. The `Token` union type covers all possible markdown elements (headings, code blocks, lists, tables, footnotes, alerts, etc.).
63
-
64
- ## Output
65
-
66
- `pantsdown.parse(markdown)` returns `{ html, javascript }`:
67
-
68
- - `html`: The rendered HTML string
69
- - `javascript`: A script for interactive features (task list checkboxes, copy buttons)
70
-
71
- ## Styling
72
-
73
- CSS is in `src/css/styles.css`. Requires a parent element with classes `pantsdown light` or `pantsdown dark` (optionally with `high-contrast`).