pantsdown 2.2.7 → 2.2.8

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/src/types.ts CHANGED
@@ -52,6 +52,8 @@ export interface Tokens extends Record<string, BaseToken> {
52
52
  raw: string;
53
53
  text: string;
54
54
  tokens: Token[];
55
+ header: boolean;
56
+ align: "center" | "left" | "right" | null;
55
57
  };
56
58
  Hr: {
57
59
  type: "hr";
@@ -103,6 +105,7 @@ export interface Tokens extends Record<string, BaseToken> {
103
105
  raw: string;
104
106
  text: string;
105
107
  tokens?: Token[];
108
+ escaped?: boolean;
106
109
  sourceMap?: SourceMap;
107
110
  };
108
111
  Def: {
@@ -119,13 +122,18 @@ export interface Tokens extends Record<string, BaseToken> {
119
122
  text: string;
120
123
  };
121
124
  Tag: {
122
- type: "text" | "html";
125
+ type: "html";
123
126
  raw: string;
124
127
  text: string;
125
128
  inLink: boolean;
126
129
  inRawBlock: boolean;
127
130
  block: boolean;
128
131
  };
132
+ Checkbox: {
133
+ type: "checkbox";
134
+ raw: string;
135
+ checked: boolean;
136
+ };
129
137
  Link: {
130
138
  type: "link";
131
139
  raw: string;
@@ -140,6 +148,7 @@ export interface Tokens extends Record<string, BaseToken> {
140
148
  text: string;
141
149
  href: string;
142
150
  title: string | null;
151
+ tokens: Token[];
143
152
  };
144
153
  Strong: {
145
154
  type: "strong";
@@ -222,6 +231,7 @@ export type Token =
222
231
  | Tokens["Codespan"]
223
232
  | Tokens["Br"]
224
233
  | Tokens["Del"]
234
+ | Tokens["Checkbox"]
225
235
  | Tokens["Alert"]
226
236
  | Tokens["Footnote"]
227
237
  | Tokens["FootnoteRef"]
package/src/utils.ts CHANGED
@@ -1,13 +1,10 @@
1
1
  import { type Lexer } from "./lexer.ts";
2
- import { type PantsdownConfig, type HTMLAttrs, type SourceMap, type Tokens } from "./types.ts";
2
+ import { other } from "./rules/other.ts";
3
+ import { type HTMLAttrs, type PantsdownConfig, type SourceMap, type Tokens } from "./types.ts";
3
4
 
4
5
  /**
5
6
  * Helpers
6
7
  */
7
- const escapeTest = /[&<>"']/;
8
- const escapeReplace = new RegExp(escapeTest.source, "g");
9
- const escapeTestNoEncode = /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/;
10
- const escapeReplaceNoEncode = new RegExp(escapeTestNoEncode.source, "g");
11
8
  const escapeReplacements: Record<string, string> = {
12
9
  "&": "&amp;",
13
10
  "<": "&lt;",
@@ -21,12 +18,12 @@ const getEscapeReplacement = (ch: string) => escapeReplacements[ch]!;
21
18
  // https://bun.sh/docs/api/utils#bun-escapehtml
22
19
  export function escape(html: string, encode?: boolean) {
23
20
  if (encode) {
24
- if (escapeTest.test(html)) {
25
- return html.replace(escapeReplace, getEscapeReplacement);
21
+ if (other.escapeTest.test(html)) {
22
+ return html.replace(other.escapeReplace, getEscapeReplacement);
26
23
  }
27
24
  } else {
28
- if (escapeTestNoEncode.test(html)) {
29
- return html.replace(escapeReplaceNoEncode, getEscapeReplacement);
25
+ if (other.escapeTestNoEncode.test(html)) {
26
+ return html.replace(other.escapeReplaceNoEncode, getEscapeReplacement);
30
27
  }
31
28
  }
32
29
 
@@ -150,7 +147,7 @@ export function fixLocalImageHref(href: string, config: PantsdownConfig): string
150
147
 
151
148
  export function cleanUrl(href: string) {
152
149
  try {
153
- href = encodeURI(href).replace(/%25/g, "%");
150
+ href = encodeURI(href).replace(other.percentDecode, "%");
154
151
  } catch (_) {
155
152
  return null;
156
153
  }
@@ -162,7 +159,7 @@ export const noopTest = { exec: () => null };
162
159
  export function splitCells(tableRow: string, count?: number) {
163
160
  // ensure that every cell-delimiting pipe has a space
164
161
  // before it to distinguish it from an escaped pipe
165
- const row = tableRow.replace(/\|/g, (_match, offset: number, str: string) => {
162
+ const row = tableRow.replace(other.findPipe, (_match, offset: number, str: string) => {
166
163
  let escaped = false;
167
164
  let curr = offset;
168
165
  while (--curr >= 0 && str[curr] === "\\") escaped = !escaped;
@@ -175,7 +172,7 @@ export function splitCells(tableRow: string, count?: number) {
175
172
  return " |";
176
173
  }
177
174
  }),
178
- cells = row.split(/ \|/);
175
+ cells = row.split(other.splitPipe);
179
176
 
180
177
  // First/last cell in a row cannot be empty if it has no leading/trailing pipe
181
178
  if (!cells[0]?.trim()) {
@@ -195,7 +192,7 @@ export function splitCells(tableRow: string, count?: number) {
195
192
 
196
193
  for (let i = 0, len = cells.length; i < len; i++) {
197
194
  // leading or trailing whitespace is ignored per the gfm spec
198
- cells[i] = cells[i]!.trim().replace(/\\\|/g, "|");
195
+ cells[i] = cells[i]!.trim().replace(other.slashPipe, "|");
199
196
  }
200
197
  return cells;
201
198
  }
@@ -232,6 +229,20 @@ export function rtrim(str: string, c: string, invert?: boolean) {
232
229
  return str.slice(0, l - suffLen);
233
230
  }
234
231
 
232
+ export function trimTrailingBlankLines(str: string) {
233
+ const lines = str.split("\n");
234
+ let end = lines.length - 1;
235
+ while (end >= 0 && other.blankLine.test(lines[end]!)) {
236
+ end--;
237
+ }
238
+ if (lines.length - end <= 2) {
239
+ // we want to keep single trailing blank lines
240
+ return str;
241
+ }
242
+
243
+ return lines.slice(0, end + 1).join("\n");
244
+ }
245
+
235
246
  export function findClosingBracket(str: string, b: string) {
236
247
  if (!b[1] || !str.includes(b[1])) {
237
248
  return -1;
@@ -250,6 +261,9 @@ export function findClosingBracket(str: string, b: string) {
250
261
  }
251
262
  }
252
263
  }
264
+ if (level > 0) {
265
+ return -2;
266
+ }
253
267
  return -1;
254
268
  }
255
269
 
@@ -260,33 +274,24 @@ export function outputLink(
260
274
  lexer: Lexer,
261
275
  ): Tokens["Link"] | Tokens["Image"] {
262
276
  const href = link.href;
263
- const title = link.title ? escape(link.title) : null;
264
- const text = cap[1]?.replace(/\\([\[\]])/g, "$1") ?? "";
265
-
266
- if (cap[0]?.charAt(0) !== "!") {
267
- lexer.state.inLink = true;
268
- const token: Tokens["Link"] = {
269
- type: "link",
270
- raw,
271
- href,
272
- title,
273
- text,
274
- tokens: lexer.inlineTokens(text),
275
- };
276
- lexer.state.inLink = false;
277
- return token;
278
- }
279
- return {
280
- type: "image",
277
+ const title = link.title || null;
278
+ const text = cap[1]?.replace(other.outputLinkReplace, "$1") ?? "";
279
+
280
+ lexer.state.inLink = true;
281
+ const token: Tokens["Link"] | Tokens["Image"] = {
282
+ type: cap[0]?.charAt(0) === "!" ? "image" : "link",
281
283
  raw,
282
284
  href,
283
285
  title,
284
- text: escape(text),
286
+ text,
287
+ tokens: lexer.inlineTokens(text),
285
288
  };
289
+ lexer.state.inLink = false;
290
+ return token;
286
291
  }
287
292
 
288
293
  export function indentCodeCompensation(raw: string, text: string) {
289
- const matchIndentToCode = /^(\s+)(?:```)/.exec(raw);
294
+ const matchIndentToCode = other.indentCodeCompensation.exec(raw);
290
295
 
291
296
  if (matchIndentToCode === null) {
292
297
  return text;
@@ -297,7 +302,7 @@ export function indentCodeCompensation(raw: string, text: string) {
297
302
  return text
298
303
  .split("\n")
299
304
  .map((node) => {
300
- const matchIndentInNode = /^\s+/.exec(node);
305
+ const matchIndentInNode = other.beginningSpace.exec(node);
301
306
  if (matchIndentInNode === null) {
302
307
  return node;
303
308
  }
@@ -344,3 +349,20 @@ export const ALERTS = [
344
349
  icon: '<svg class="octicon octicon-alert" style="margin-right: 0.5rem;" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"/></svg>',
345
350
  },
346
351
  ];
352
+
353
+ export function expandTabs(line: string, indent = 0) {
354
+ let col = indent;
355
+ let expanded = "";
356
+ for (const char of line) {
357
+ if (char === "\t") {
358
+ const added = 4 - (col % 4);
359
+ expanded += " ".repeat(added);
360
+ col += added;
361
+ } else {
362
+ expanded += char;
363
+ col++;
364
+ }
365
+ }
366
+
367
+ return expanded;
368
+ }