feat: html and markdown tagged template helpers

This commit is contained in:
teidesu 2021-07-02 20:20:29 +03:00
parent 03cb8fd5e8
commit 733a1ab84f
6 changed files with 71 additions and 14 deletions

View file

@ -13,17 +13,14 @@ API ([documented here](https://core.telegram.org/bots/api#html-style))
```typescript
import { TelegramClient } from '@mtcute/client'
import { HtmlMessageEntityParser } from '@mtcute/html-parser'
import { HtmlMessageEntityParser, html } from '@mtcute/html-parser'
const tg = new TelegramClient({ ... })
tg.registerParseMode(new HtmlMessageEntityParser())
tg.sendText(
'me',
'Hello, <b>me</b>! Updates from the feed:\n' +
HtmlMessageEntityParser.escape(
await getUpdatesFromFeed()
)
html`Hello, <b>me</b>! Updates from the feed:\n${await getUpdatesFromFeed()}`
)
```
@ -94,4 +91,13 @@ bold _and_** _italic_
Escaping in this parser works exactly the same as in `htmlparser2`.
This means that you can keep `<>&` symbols as-is in some cases. However, when dealing with user input, it is always
better to use [`HtmlMessageEntityParser.escape`](./classes/htmlmessageentityparser.html#escape)
better to use [`HtmlMessageEntityParser.escape`](./classes/htmlmessageentityparser.html#escape) or, even better,
`html` helper:
```typescript
import { html } from '@mtcute/html-parser'
const username = 'Boris <&>'
const text = html`Hi, ${username}!`
console.log(text) // Hi, Boris &amp;lt;&amp;amp;&amp;gt;!
```

View file

@ -5,6 +5,22 @@ import bigInt from 'big-integer'
const MENTION_REGEX = /^tg:\/\/user\?id=(\d+)(?:&hash=(-?[0-9a-fA-F]+)(?:&|$)|&|$)/
/**
* Tagged template based helper for escaping entities in HTML
*
* @example
* ```typescript
* const escaped = html`<b>${user.displayName}</b>`
* ```
*/
export function html(strings: TemplateStringsArray, ...sub: string[]): string {
let str = ''
sub.forEach((it, idx) => {
str += strings[idx] + HtmlMessageEntityParser.escape(it)
})
return str + strings[strings.length - 1]
}
export namespace HtmlMessageEntityParser {
/**
* Syntax highlighter function used in {@link HtmlMessageEntityParser.unparse}

View file

@ -1,7 +1,7 @@
import { describe, it } from 'mocha'
import { expect } from 'chai'
import { tl } from '@mtcute/tl'
import { HtmlMessageEntityParser } from '../src'
import { HtmlMessageEntityParser, html } from '../src'
import { MessageEntity } from '@mtcute/client'
import bigInt from 'big-integer'
@ -450,4 +450,15 @@ describe('HtmlMessageEntityParser', () => {
test('<a href="">link</a> <a>link</a>', [], 'link link')
})
})
describe('template', () => {
it('should work as a tagged template literal', () => {
const unsafeString = '<&>'
expect(html`${unsafeString}`).eq('&lt;&amp;&gt;')
expect(html`${unsafeString} <b>text</b>`).eq('&lt;&amp;&gt; <b>text</b>')
expect(html`<b>text</b> ${unsafeString}`).eq('<b>text</b> &lt;&amp;&gt;')
expect(html`<b>${unsafeString}</b>`).eq('<b>&lt;&amp;&gt;</b>')
})
})
})

View file

@ -12,17 +12,14 @@ This package implements formatting syntax similar to Markdown (CommonMark) but s
```typescript
import { TelegramClient } from '@mtcute/client'
import { MarkdownMessageEntityParser } from '@mtcute/markdown-parser'
import { MarkdownMessageEntityParser, md } from '@mtcute/markdown-parser'
const tg = new TelegramClient({ ... })
tg.registerParseMode(new MarkdownMessageEntityParser())
tg.sendText(
'me',
'Hello, **me**! Updates from the feed:\n' +
MarkdownMessageEntityParser.escape(
await getUpdatesFromFeed()
)
md`Hello, **me**! Updates from the feed:\n${await getUpdatesFromFeed()}`
)
```
@ -126,7 +123,7 @@ like `"\\_\\_not italic\\_\\_`.
> **Note**: backslash itself must be escaped like this: ` \\ ` (double backslash).
>
> This will look pretty bad in real code, so use escaping only when really needed, and use
> [`MarkdownMessageEntityParser.escape`](./classes/markdownmessageentityparser.html#escape) or
> [`MarkdownMessageEntityParser.escape`](./classes/markdownmessageentityparser.html#escape) or `md` or
> other parse modes (like HTML one provided by [`@mtcute/html-parser`](../html-parser/index.html))) instead.
> In theory, you could escape every single non-markup character, but why would you want to do that 😜

View file

@ -13,6 +13,22 @@ const TAG_PRE = '```'
const TO_BE_ESCAPED = /[*_\-~`[\\\]]/g
/**
* Tagged template based helper for escaping entities in Markdown
*
* @example
* ```typescript
* const escaped = md`**${user.displayName}**`
* ```
*/
export function md(strings: TemplateStringsArray, ...sub: string[]): string {
let str = ''
sub.forEach((it, idx) => {
str += strings[idx] + MarkdownMessageEntityParser.escape(it)
})
return str + strings[strings.length - 1]
}
/**
* Markdown MessageEntity parser.
*

View file

@ -2,7 +2,7 @@ import { describe, it } from 'mocha'
import { expect } from 'chai'
import { tl } from '@mtcute/tl'
import { MessageEntity } from '@mtcute/client'
import { MarkdownMessageEntityParser } from '../src'
import { MarkdownMessageEntityParser, md } from '../src'
import bigInt from 'big-integer'
const createEntity = <T extends tl.TypeMessageEntity['_']>(
@ -647,4 +647,15 @@ describe('MarkdownMessageEntityParser', () => {
})
})
})
describe('template', () => {
it('should work as a tagged template literal', () => {
const unsafeString = '__[]__'
expect(md`${unsafeString}`).eq('\\_\\_\\[\\]\\_\\_')
expect(md`${unsafeString} **text**`).eq('\\_\\_\\[\\]\\_\\_ **text**')
expect(md`**text** ${unsafeString}`).eq('**text** \\_\\_\\[\\]\\_\\_')
expect(md`**${unsafeString}**`).eq('**\\_\\_\\[\\]\\_\\_**')
})
})
})