@mtcute/markdown-parser 0.1.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.
package/LICENSE ADDED
@@ -0,0 +1,8 @@
1
+ Copyright 2023 Alina Sireneva
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the โ€œSoftwareโ€), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED โ€œAS ISโ€, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8
+
package/README.md ADDED
@@ -0,0 +1,138 @@
1
+ # @mtcute/markdown-parser
2
+
3
+ ๐Ÿ“– [API Reference](https://ref.mtcute.dev/modules/_mtcute_markdown_parser.html)
4
+
5
+ Markdown entities parser for mtcute
6
+
7
+ > **NOTE**: The syntax implemented here is **not** compatible with Bot API _Markdown_, nor _MarkdownV2_.
8
+ >
9
+ > Please read [Syntax](#syntax) below for a detailed explanation
10
+
11
+ ## Features
12
+ - Supports all entities that Telegram supports
13
+ - Supports nested and overlapping entities
14
+ - Supports dedentation
15
+ - [Interpolation](#interpolation)!
16
+
17
+ ## Usage
18
+
19
+ ```typescript
20
+ import { md } from '@mtcute/markdown-parser'
21
+
22
+ tg.sendText(
23
+ 'me',
24
+ md`
25
+ Hello, **me**! Updates from the feed:
26
+ ${await getUpdatesFromFeed()}
27
+ `
28
+ )
29
+ ```
30
+
31
+ ## Syntax
32
+
33
+ ### Inline entities
34
+
35
+ Inline entities are defined by some _tag_ surrounding some text, and processing them simply strips their tag.
36
+
37
+ Supported entities:
38
+
39
+ - Bold, tag is `**`
40
+ - Italic, tag is `__`
41
+ - Underline, tag is `--` (_NON-STANDARD_)
42
+ - Strikethrough, tag is `~~`
43
+ - Spoiler, tag is `||` (_NON-STANDARD_)
44
+ - Code (monospaced font), tag is <code>`</code>
45
+ - Note that escaping text works differently inside code, see below.
46
+
47
+ > Unlike CommonMark, we use the symbol itself and not its count.
48
+ > Thus, using `*` (asterisk) will **always** produce bold,
49
+ > and using `_` (underscore) will **always** produce italic.
50
+ >
51
+ > This eliminates a lot of confusion, like: `_bold_` โ†’ _bold_, `**italic**` โ†’ **italic**
52
+
53
+ | Code | Result (visual) | Result (as HTML) |
54
+ |----------------------------------------------|-------------------|------------------------------|
55
+ | `**bold**` | **bold** | `<b>bold</b>` |
56
+ | `__italic__` | __italic__ | `<i>italic</i>` |
57
+ | `--underline--` | <u>underline</u> | `<u>underline</u>` |
58
+ | `~~strikethrough~~` | ~~strikethrough~~ | `<s>strikethrough</s>` |
59
+ | <code>&#124;&#124;spoiler&#124;&#124;</code> | N/A | `<spoiler>spoiler</spoiler>` |
60
+ | `*whatever*` | \*whatever\* | `*whatever*` |
61
+ | `_whatever_` | \_whatever\_ | `_whatever_` |
62
+ | <code>\`hello world\`</code> | `hello world` | `<code>hello world</code>` |
63
+ | <code>\`__text__\`</code> | `__text__` | `<code>__text__</code>` |
64
+
65
+ ### Pre
66
+
67
+ Pre represents a single block of code, optionally with a language.
68
+
69
+ This entity starts with <code>\`\`\`</code> (triple backtick), optionally followed with language name and a must be
70
+ followed with a line break, and ends with <code>\`\`\`</code> (triple backtick), optionally preceded with a line break.
71
+
72
+ | Code | Result (visual) | Result (as HTML) |
73
+ |--------------------------------------------------------------------|---------------------------|---------------------------------------------------------------------------------------------|
74
+ | <pre><code>\`\`\`<br>hello<br>\`\`\`</code></pre> | `hello` | `<pre>hello</pre>` |
75
+ | <pre><code>\`\`\`<br>hello\`\`\`</code></pre> | `hello` | `<pre>hello</pre>` |
76
+ | <pre><code>\`\`\`javascript<br>const a = ``<br>\`\`\`</code></pre> | <code>const a = ``</code> | <pre><code>&lt;pre language="javascript"&gt;<br> const a = ``<br>&lt;/pre&gt;</code></pre> |
77
+
78
+ ### Links
79
+
80
+ Links are parsed exactly the same as standard markdown (except references are not supported).
81
+
82
+ Defined like this: `[Link text](https://example.com)`.
83
+
84
+ - Link text may also contain any formatting, but link cannot contain other links inside (obviously).
85
+ - `[` (opening square bracket) inside link text will be treated like a normal character.
86
+
87
+ A markdown-style link can also be used to define a name mention like this: `[Name](tg://user?id=1234567)`,
88
+ where `1234567` is the ID of the user you want to mention.
89
+
90
+ Additionally, a markdown-style link can be used to define a custom emoji like this:
91
+ `[๐Ÿ˜„](tg://emoji?id=123456)`, where `123456` is ID of the emoji.
92
+
93
+ > **Note**: It is up to the client to look up user's input entity by ID.
94
+ > In most cases, you can only use IDs of users that were seen by the client while using given storage.
95
+ >
96
+ > Alternatively, you can explicitly provide access hash like this: `[Name](tg://user?id=1234567&hash=abc`,
97
+ > where `abc` is user's access hash written as a base-16 *unsigned* integer.
98
+ > Order of the parameters does matter, i.e. `tg://user?hash=abc&id=1234567` will not be processed as expected.
99
+
100
+ | Code | Result (visual) | Result (as HTML) |
101
+ |------------------------------------|--------------------------------|--------------------------------------------------|
102
+ | `[Google](https://google.com)` | [Google](https://google.com) | `<a href="https://google.com">Google</a>` |
103
+ | `[__Google__](https://google.com)` | [_Google_](https://google.com) | `<a href="https://google.com"><i>Google</i></a>` |
104
+ | `[empty link]()` | empty link | `empty link` |
105
+ | `[empty link]` | [empty link] | `[empty link]` |
106
+ | `[User](tg://user?id=1234567)` | N/A | N/A |
107
+ | `[๐Ÿ˜„](tg://emoji?id=123456)` | N/A | N/A |
108
+
109
+ ### Nested and overlapping entities
110
+
111
+ Quite a powerful feature of this parser is the ability to process overlapping entities. Only inline entities (except
112
+ code) can be overlapped.
113
+
114
+ Since inline entities are only defined by their tag, and nesting same entities doesn't make sense, you can think of the
115
+ tags just as start/end markers, and not in terms of nesting.
116
+
117
+ | Code | Result (visual) | Result (as HTML) |
118
+ |-------------------------------|---------------------------|----------------------------------------|
119
+ | `**Welcome back, __User__!**` | **Welcome back, _User_!** | `<b>Welcome back, <i>User</i>!</b>` |
120
+ | `**bold __and** italic__` | **bold _and_** _italic_ | `<b>bold <i>and</i></b><i> italic</i>` |
121
+
122
+ ## Interpolation
123
+
124
+ Being a tagged template literal, `md` supports interpolation.
125
+
126
+ You can interpolate one of the following:
127
+ - `string` - **will not** be parsed, and appended to plain text as-is
128
+ - In case you want the string to be parsed, use `md` as a simple function: <code>md\`... ${md('**bold**')} ...\`</code>
129
+ - `number` - will be converted to string and appended to plain text as-is
130
+ - `TextWithEntities` or `MessageEntity` - will add the text and its entities to the output. This is the type returned by `md` itself:
131
+ ```ts
132
+ const bold = md`**bold**`
133
+ const text = md`Hello, ${bold}!`
134
+ ```
135
+ - falsy value (i.e. `null`, `undefined`, `false`) - will be ignored
136
+
137
+ Because of interpolation, you almost never need to think about escaping anything,
138
+ since the values are not even parsed as Markdown, and are appended to the output as-is.
package/cjs/index.d.ts ADDED
@@ -0,0 +1,46 @@
1
+ import type { InputText, MessageEntity, TextWithEntities } from '@mtcute/client';
2
+ /**
3
+ * Escape a string to be safely used in Markdown.
4
+ *
5
+ * > **Note**: this function is in most cases not needed, as `md` function
6
+ * > handles all `string`s passed to it automatically as plain text.
7
+ */
8
+ declare function escape(str: string): string;
9
+ /**
10
+ * Add Markdown formatting to the text given the plain text and entities contained in it.
11
+ */
12
+ declare function unparse(input: InputText): string;
13
+ export declare const md: {
14
+ /**
15
+ * Tagged template based Markdown-to-entities parser function
16
+ *
17
+ * Additionally, `md` function has two static methods:
18
+ * - `md.escape` - escape a string to be safely used in Markdown
19
+ * (should not be needed in most cases, as `md` function itself handles all `string`s
20
+ * passed to it automatically as plain text)
21
+ * - `md.unparse` - add Markdown formatting to the text given the plain text and entities contained in it
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * const text = md`**${user.displayName}**`
26
+ * ```
27
+ */
28
+ (strings: TemplateStringsArray, ...sub: (InputText | MessageEntity | boolean | number | undefined | null)[]): TextWithEntities;
29
+ /**
30
+ * A variant taking a plain JS string as input
31
+ * and parsing it.
32
+ *
33
+ * Useful for cases when you already have a string
34
+ * (e.g. from some server) and want to parse it.
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * const string = '**hello**'
39
+ * const text = md(string)
40
+ * ```
41
+ */
42
+ (string: string): TextWithEntities;
43
+ escape: typeof escape;
44
+ unparse: typeof unparse;
45
+ };
46
+ export {};
package/cjs/index.js ADDED
@@ -0,0 +1,381 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.md = void 0;
7
+ const long_1 = __importDefault(require("long"));
8
+ const MENTION_REGEX = /^tg:\/\/user\?id=(\d+)(?:&hash=(-?[0-9a-fA-F]+)(?:&|$)|&|$)/;
9
+ const EMOJI_REGEX = /^tg:\/\/emoji\?id=(-?\d+)/;
10
+ const TAG_BOLD = '**';
11
+ const TAG_ITALIC = '__';
12
+ const TAG_UNDERLINE = '--';
13
+ const TAG_STRIKE = '~~';
14
+ const TAG_SPOILER = '||';
15
+ const TAG_CODE = '`';
16
+ const TAG_PRE = '```';
17
+ const TO_BE_ESCAPED = /[*_\-~`[\\\]|]/g;
18
+ /**
19
+ * Escape a string to be safely used in Markdown.
20
+ *
21
+ * > **Note**: this function is in most cases not needed, as `md` function
22
+ * > handles all `string`s passed to it automatically as plain text.
23
+ */
24
+ function escape(str) {
25
+ return str.replace(TO_BE_ESCAPED, (s) => '\\' + s);
26
+ }
27
+ /**
28
+ * Add Markdown formatting to the text given the plain text and entities contained in it.
29
+ */
30
+ function unparse(input) {
31
+ if (typeof input === 'string')
32
+ return escape(input);
33
+ let text = input.text;
34
+ const entities = input.entities ?? [];
35
+ // keep track of positions of inserted escape symbols
36
+ const escaped = [];
37
+ text = text.replace(TO_BE_ESCAPED, (s, pos) => {
38
+ escaped.push(pos);
39
+ return '\\' + s;
40
+ });
41
+ const hasEscaped = escaped.length > 0;
42
+ const insert = [];
43
+ for (const entity of entities) {
44
+ const type = entity._;
45
+ let start = entity.offset;
46
+ let end = start + entity.length;
47
+ if (start > text.length)
48
+ continue;
49
+ if (start < 0)
50
+ start = 0;
51
+ if (end > text.length)
52
+ end = text.length;
53
+ if (hasEscaped) {
54
+ // determine number of escape chars since the beginning of the string
55
+ let escapedPos = 0;
56
+ while (escapedPos < escaped.length && escaped[escapedPos] < start) {
57
+ escapedPos += 1;
58
+ }
59
+ start += escapedPos;
60
+ while (escapedPos < escaped.length && escaped[escapedPos] <= end) {
61
+ escapedPos += 1;
62
+ }
63
+ end += escapedPos;
64
+ }
65
+ let startTag;
66
+ let endTag;
67
+ switch (type) {
68
+ case 'messageEntityBold':
69
+ startTag = endTag = TAG_BOLD;
70
+ break;
71
+ case 'messageEntityItalic':
72
+ startTag = endTag = TAG_ITALIC;
73
+ break;
74
+ case 'messageEntityUnderline':
75
+ startTag = endTag = TAG_UNDERLINE;
76
+ break;
77
+ case 'messageEntityStrike':
78
+ startTag = endTag = TAG_STRIKE;
79
+ break;
80
+ case 'messageEntitySpoiler':
81
+ startTag = endTag = TAG_SPOILER;
82
+ break;
83
+ case 'messageEntityCode':
84
+ startTag = endTag = TAG_CODE;
85
+ break;
86
+ case 'messageEntityPre':
87
+ startTag = TAG_PRE;
88
+ if (entity.language) {
89
+ startTag += entity.language;
90
+ }
91
+ startTag += '\n';
92
+ endTag = '\n' + TAG_PRE;
93
+ break;
94
+ case 'messageEntityTextUrl':
95
+ startTag = '[';
96
+ endTag = `](${entity.url})`;
97
+ break;
98
+ case 'messageEntityMentionName':
99
+ startTag = '[';
100
+ endTag = `](tg://user?id=${entity.userId})`;
101
+ break;
102
+ case 'messageEntityCustomEmoji':
103
+ startTag = '[';
104
+ endTag = `](tg://emoji?id=${entity.documentId.toString()})`;
105
+ break;
106
+ default:
107
+ continue;
108
+ }
109
+ insert.push([start, startTag]);
110
+ insert.push([end, endTag]);
111
+ }
112
+ // sort by offset desc
113
+ insert.sort((a, b) => b[0] - a[0]);
114
+ for (const [offset, tag] of insert) {
115
+ text = text.substr(0, offset) + tag + text.substr(offset);
116
+ }
117
+ return text;
118
+ }
119
+ function parse(strings, ...sub) {
120
+ const entities = [];
121
+ let result = '';
122
+ const stacks = {};
123
+ let insideCode = false;
124
+ let insidePre = false;
125
+ let insideLink = false;
126
+ function feed(text) {
127
+ const len = text.length;
128
+ let pos = 0;
129
+ while (pos < len) {
130
+ const c = text[pos];
131
+ if (c === '\\') {
132
+ result += text[pos + 1];
133
+ pos += 2;
134
+ continue;
135
+ }
136
+ if (insideCode) {
137
+ if (c === '`') {
138
+ // we can be certain that we're inside code
139
+ const ent = stacks.code.pop();
140
+ ent.length = result.length - ent.offset;
141
+ entities.push(ent);
142
+ insideCode = false;
143
+ pos += 1;
144
+ }
145
+ else {
146
+ pos += 1;
147
+ result += c;
148
+ }
149
+ continue;
150
+ }
151
+ if (insidePre) {
152
+ if (c === '`' || (c === '\n' && text[pos + 1] === '`')) {
153
+ if (c === '\n')
154
+ pos += 1;
155
+ if (text[pos + 1] === '`' && text[pos + 2] === '`') {
156
+ // we can be certain that we're inside pre
157
+ const ent = stacks.pre.pop();
158
+ ent.length = result.length - ent.offset;
159
+ entities.push(ent);
160
+ insidePre = false;
161
+ pos += 3;
162
+ continue;
163
+ // closed with single or double backtick
164
+ // i.e. not closed actually! this is totally valid md:
165
+ // ```javascript
166
+ // const a = ``;
167
+ // ```
168
+ // compensate that `pos` change we made earliers
169
+ }
170
+ else if (c === '\n') {
171
+ pos -= 1;
172
+ }
173
+ }
174
+ pos += 1;
175
+ result += c;
176
+ continue;
177
+ }
178
+ if (insideLink && c === ']') {
179
+ // we can be certain that we're inside link
180
+ const ent = stacks.link.pop();
181
+ if (text[pos + 1] !== '(') {
182
+ // [link text]
183
+ // ignore this, and add opening [
184
+ result = `${result.substr(0, ent.offset)}[${result.substr(ent.offset)}]`;
185
+ pos += 1;
186
+ insideLink = false;
187
+ continue;
188
+ }
189
+ pos += 2;
190
+ let url = '';
191
+ while (pos < text.length && text[pos] !== ')') {
192
+ url += text[pos++];
193
+ }
194
+ pos += 1; // )
195
+ if (pos > text.length) {
196
+ throw new Error('Malformed LINK entity, expected )');
197
+ }
198
+ if (url.length) {
199
+ ent.length = result.length - ent.offset;
200
+ let m = url.match(MENTION_REGEX);
201
+ if (m) {
202
+ const userId = parseInt(m[1]);
203
+ const accessHash = m[2];
204
+ if (accessHash) {
205
+ ent._ =
206
+ 'inputMessageEntityMentionName';
207
+ ent.userId = {
208
+ _: 'inputUser',
209
+ userId,
210
+ accessHash: long_1.default.fromString(accessHash, false, 16),
211
+ };
212
+ }
213
+ else {
214
+ ent._ = 'messageEntityMentionName';
215
+ ent.userId = userId;
216
+ }
217
+ }
218
+ else if ((m = EMOJI_REGEX.exec(url))) {
219
+ ent._ = 'messageEntityCustomEmoji';
220
+ ent.documentId = long_1.default.fromString(m[1]);
221
+ }
222
+ else {
223
+ if (url.match(/^\/\//))
224
+ url = 'http:' + url;
225
+ ent._ = 'messageEntityTextUrl';
226
+ ent.url = url;
227
+ }
228
+ entities.push(ent);
229
+ }
230
+ insideLink = false;
231
+ continue;
232
+ }
233
+ if (c === '[' && !insideLink) {
234
+ pos += 1;
235
+ insideLink = true;
236
+ if (!('link' in stacks))
237
+ stacks.link = [];
238
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
239
+ stacks.link.push({
240
+ offset: result.length,
241
+ length: 0,
242
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
243
+ }); // other fields are added after the second part
244
+ continue;
245
+ }
246
+ if (c === '`') {
247
+ const isPre = text[pos + 1] === '`' && text[pos + 2] === '`';
248
+ if (isPre) {
249
+ pos += 3;
250
+ let language = '';
251
+ while (pos < text.length && text[pos] !== '\n') {
252
+ language += text[pos++];
253
+ }
254
+ // newline
255
+ pos += 1;
256
+ if (pos > text.length) {
257
+ throw new Error('Malformed PRE entity, expected LF after ```');
258
+ }
259
+ if (!('pre' in stacks))
260
+ stacks.pre = [];
261
+ stacks.pre.push({
262
+ _: 'messageEntityPre',
263
+ offset: result.length,
264
+ length: 0,
265
+ language,
266
+ });
267
+ insidePre = true;
268
+ }
269
+ else {
270
+ pos += 1;
271
+ if (!('code' in stacks))
272
+ stacks.code = [];
273
+ stacks.code.push({
274
+ _: 'messageEntityCode',
275
+ offset: result.length,
276
+ length: 0,
277
+ });
278
+ insideCode = true;
279
+ }
280
+ continue;
281
+ }
282
+ if (c === text[pos + 1]) {
283
+ // maybe (?) start or end of an entity
284
+ let type = null;
285
+ switch (c) {
286
+ case '_':
287
+ type = 'Italic';
288
+ break;
289
+ case '*':
290
+ type = 'Bold';
291
+ break;
292
+ case '-':
293
+ type = 'Underline';
294
+ break;
295
+ case '~':
296
+ type = 'Strike';
297
+ break;
298
+ case '|':
299
+ type = 'Spoiler';
300
+ break;
301
+ }
302
+ if (type) {
303
+ if (!(type in stacks))
304
+ stacks[type] = [];
305
+ const isBegin = stacks[type].length === 0;
306
+ if (isBegin) {
307
+ stacks[type].push({
308
+ _: `messageEntity${type}`,
309
+ offset: result.length,
310
+ length: 0,
311
+ });
312
+ }
313
+ else {
314
+ // valid because isBegin is false
315
+ const ent = stacks[type].pop();
316
+ ent.length = result.length - ent.offset;
317
+ entities.push(ent);
318
+ }
319
+ pos += 2;
320
+ continue;
321
+ }
322
+ }
323
+ if (c === '\n') {
324
+ if (pos !== 0) {
325
+ result += '\n';
326
+ }
327
+ const nonWhitespace = text.slice(pos + 1).search(/\S/);
328
+ if (nonWhitespace !== -1) {
329
+ pos += nonWhitespace + 1;
330
+ }
331
+ else {
332
+ pos = len;
333
+ result = result.trimEnd();
334
+ }
335
+ continue;
336
+ }
337
+ // nothing matched => normal character
338
+ result += c;
339
+ pos += 1;
340
+ }
341
+ }
342
+ if (typeof strings === 'string')
343
+ strings = [strings];
344
+ sub.forEach((it, idx) => {
345
+ feed(strings[idx]);
346
+ if (typeof it === 'boolean' || !it)
347
+ return;
348
+ if (typeof it === 'string' || typeof it === 'number') {
349
+ result += it;
350
+ }
351
+ else {
352
+ // TextWithEntities or MessageEntity
353
+ const text = it.text;
354
+ const innerEntities = 'raw' in it ? [it.raw] : it.entities;
355
+ const baseOffset = result.length;
356
+ result += text;
357
+ if (innerEntities) {
358
+ for (const ent of innerEntities) {
359
+ entities.push({ ...ent, offset: ent.offset + baseOffset });
360
+ }
361
+ }
362
+ }
363
+ });
364
+ feed(strings[strings.length - 1]);
365
+ for (const [name, stack] of Object.entries(stacks)) {
366
+ if (stack.length) {
367
+ throw new Error(`Unterminated ${name} entity`);
368
+ }
369
+ }
370
+ return {
371
+ text: result,
372
+ entities,
373
+ };
374
+ }
375
+ // typedoc doesn't support this yet, so we'll have to do it manually
376
+ // https://github.com/TypeStrong/typedoc/issues/2436
377
+ exports.md = Object.assign(parse, {
378
+ escape,
379
+ unparse,
380
+ });
381
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import Long from 'long'\n\nimport type { InputText, MessageEntity, TextWithEntities, tl } from '@mtcute/client'\n\nconst MENTION_REGEX = /^tg:\\/\\/user\\?id=(\\d+)(?:&hash=(-?[0-9a-fA-F]+)(?:&|$)|&|$)/\nconst EMOJI_REGEX = /^tg:\\/\\/emoji\\?id=(-?\\d+)/\n\nconst TAG_BOLD = '**'\nconst TAG_ITALIC = '__'\nconst TAG_UNDERLINE = '--'\nconst TAG_STRIKE = '~~'\nconst TAG_SPOILER = '||'\nconst TAG_CODE = '`'\nconst TAG_PRE = '```'\n\nconst TO_BE_ESCAPED = /[*_\\-~`[\\\\\\]|]/g\n\n/**\n * Escape a string to be safely used in Markdown.\n *\n * > **Note**: this function is in most cases not needed, as `md` function\n * > handles all `string`s passed to it automatically as plain text.\n */\nfunction escape(str: string): string {\n return str.replace(TO_BE_ESCAPED, (s) => '\\\\' + s)\n}\n\n/**\n * Add Markdown formatting to the text given the plain text and entities contained in it.\n */\nfunction unparse(input: InputText): string {\n if (typeof input === 'string') return escape(input)\n\n let text = input.text\n const entities = input.entities ?? []\n\n // keep track of positions of inserted escape symbols\n const escaped: number[] = []\n text = text.replace(TO_BE_ESCAPED, (s, pos: number) => {\n escaped.push(pos)\n\n return '\\\\' + s\n })\n const hasEscaped = escaped.length > 0\n\n type InsertLater = [number, string]\n const insert: InsertLater[] = []\n\n for (const entity of entities) {\n const type = entity._\n\n let start = entity.offset\n let end = start + entity.length\n\n if (start > text.length) continue\n if (start < 0) start = 0\n if (end > text.length) end = text.length\n\n if (hasEscaped) {\n // determine number of escape chars since the beginning of the string\n let escapedPos = 0\n\n while (escapedPos < escaped.length && escaped[escapedPos] < start) {\n escapedPos += 1\n }\n start += escapedPos\n\n while (escapedPos < escaped.length && escaped[escapedPos] <= end) {\n escapedPos += 1\n }\n end += escapedPos\n }\n\n let startTag\n let endTag: string\n\n switch (type) {\n case 'messageEntityBold':\n startTag = endTag = TAG_BOLD\n break\n case 'messageEntityItalic':\n startTag = endTag = TAG_ITALIC\n break\n case 'messageEntityUnderline':\n startTag = endTag = TAG_UNDERLINE\n break\n case 'messageEntityStrike':\n startTag = endTag = TAG_STRIKE\n break\n case 'messageEntitySpoiler':\n startTag = endTag = TAG_SPOILER\n break\n case 'messageEntityCode':\n startTag = endTag = TAG_CODE\n break\n case 'messageEntityPre':\n startTag = TAG_PRE\n\n if (entity.language) {\n startTag += entity.language\n }\n\n startTag += '\\n'\n endTag = '\\n' + TAG_PRE\n break\n case 'messageEntityTextUrl':\n startTag = '['\n endTag = `](${entity.url})`\n break\n case 'messageEntityMentionName':\n startTag = '['\n endTag = `](tg://user?id=${entity.userId})`\n break\n case 'messageEntityCustomEmoji':\n startTag = '['\n endTag = `](tg://emoji?id=${entity.documentId.toString()})`\n break\n default:\n continue\n }\n\n insert.push([start, startTag])\n insert.push([end, endTag])\n }\n\n // sort by offset desc\n insert.sort((a, b) => b[0] - a[0])\n\n for (const [offset, tag] of insert) {\n text = text.substr(0, offset) + tag + text.substr(offset)\n }\n\n return text\n}\n\nfunction parse(\n strings: TemplateStringsArray | string,\n ...sub: (InputText | MessageEntity | boolean | number | undefined | null)[]\n): TextWithEntities {\n const entities: tl.TypeMessageEntity[] = []\n let result = ''\n\n const stacks: Record<string, tl.Mutable<tl.TypeMessageEntity>[]> = {}\n\n let insideCode = false\n let insidePre = false\n let insideLink = false\n\n function feed(text: string) {\n const len = text.length\n let pos = 0\n\n while (pos < len) {\n const c = text[pos]\n\n if (c === '\\\\') {\n result += text[pos + 1]\n pos += 2\n continue\n }\n\n if (insideCode) {\n if (c === '`') {\n // we can be certain that we're inside code\n\n const ent = stacks.code.pop()!\n ent.length = result.length - ent.offset\n entities.push(ent)\n insideCode = false\n pos += 1\n } else {\n pos += 1\n result += c\n }\n continue\n }\n\n if (insidePre) {\n if (c === '`' || (c === '\\n' && text[pos + 1] === '`')) {\n if (c === '\\n') pos += 1\n\n if (text[pos + 1] === '`' && text[pos + 2] === '`') {\n // we can be certain that we're inside pre\n\n const ent = stacks.pre.pop()!\n ent.length = result.length - ent.offset\n entities.push(ent)\n insidePre = false\n pos += 3\n continue\n\n // closed with single or double backtick\n // i.e. not closed actually! this is totally valid md:\n // ```javascript\n // const a = ``;\n // ```\n // compensate that `pos` change we made earliers\n } else if (c === '\\n') {\n pos -= 1\n }\n }\n\n pos += 1\n result += c\n continue\n }\n\n if (insideLink && c === ']') {\n // we can be certain that we're inside link\n\n const ent = stacks.link.pop()!\n\n if (text[pos + 1] !== '(') {\n // [link text]\n // ignore this, and add opening [\n result = `${result.substr(0, ent.offset)}[${result.substr(ent.offset)}]`\n pos += 1\n insideLink = false\n continue\n }\n\n pos += 2\n let url = ''\n\n while (pos < text.length && text[pos] !== ')') {\n url += text[pos++]\n }\n\n pos += 1 // )\n\n if (pos > text.length) {\n throw new Error('Malformed LINK entity, expected )')\n }\n\n if (url.length) {\n ent.length = result.length - ent.offset\n\n let m = url.match(MENTION_REGEX)\n\n if (m) {\n const userId = parseInt(m[1])\n const accessHash = m[2]\n\n if (accessHash) {\n (ent as tl.Mutable<tl.RawInputMessageEntityMentionName>)._ =\n 'inputMessageEntityMentionName'\n ;(ent as tl.Mutable<tl.RawInputMessageEntityMentionName>).userId = {\n _: 'inputUser',\n userId,\n accessHash: Long.fromString(accessHash, false, 16),\n }\n } else {\n (ent as tl.Mutable<tl.RawMessageEntityMentionName>)._ = 'messageEntityMentionName'\n ;(ent as tl.Mutable<tl.RawMessageEntityMentionName>).userId = userId\n }\n } else if ((m = EMOJI_REGEX.exec(url))) {\n (ent as tl.Mutable<tl.RawMessageEntityCustomEmoji>)._ = 'messageEntityCustomEmoji'\n ;(ent as tl.Mutable<tl.RawMessageEntityCustomEmoji>).documentId = Long.fromString(m[1])\n } else {\n if (url.match(/^\\/\\//)) url = 'http:' + url\n ;(ent as tl.Mutable<tl.RawMessageEntityTextUrl>)._ = 'messageEntityTextUrl'\n ;(ent as tl.Mutable<tl.RawMessageEntityTextUrl>).url = url\n }\n entities.push(ent)\n }\n\n insideLink = false\n continue\n }\n\n if (c === '[' && !insideLink) {\n pos += 1\n insideLink = true\n if (!('link' in stacks)) stacks.link = []\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n stacks.link.push({\n offset: result.length,\n length: 0,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } as any) // other fields are added after the second part\n continue\n }\n\n if (c === '`') {\n const isPre = text[pos + 1] === '`' && text[pos + 2] === '`'\n\n if (isPre) {\n pos += 3\n let language = ''\n\n while (pos < text.length && text[pos] !== '\\n') {\n language += text[pos++]\n }\n\n // newline\n pos += 1\n\n if (pos > text.length) {\n throw new Error('Malformed PRE entity, expected LF after ```')\n }\n\n if (!('pre' in stacks)) stacks.pre = []\n stacks.pre.push({\n _: 'messageEntityPre',\n offset: result.length,\n length: 0,\n language,\n })\n insidePre = true\n } else {\n pos += 1\n if (!('code' in stacks)) stacks.code = []\n stacks.code.push({\n _: 'messageEntityCode',\n offset: result.length,\n length: 0,\n })\n insideCode = true\n }\n\n continue\n }\n\n if (c === text[pos + 1]) {\n // maybe (?) start or end of an entity\n let type: 'Italic' | 'Bold' | 'Underline' | 'Strike' | 'Spoiler' | null = null\n\n switch (c) {\n case '_':\n type = 'Italic'\n break\n case '*':\n type = 'Bold'\n break\n case '-':\n type = 'Underline'\n break\n case '~':\n type = 'Strike'\n break\n case '|':\n type = 'Spoiler'\n break\n }\n\n if (type) {\n if (!(type in stacks)) stacks[type] = []\n const isBegin = stacks[type].length === 0\n\n if (isBegin) {\n stacks[type].push({\n _: `messageEntity${type}`,\n offset: result.length,\n length: 0,\n })\n } else {\n // valid because isBegin is false\n\n const ent = stacks[type].pop()!\n ent.length = result.length - ent.offset\n entities.push(ent)\n }\n\n pos += 2\n continue\n }\n }\n\n if (c === '\\n') {\n if (pos !== 0) {\n result += '\\n'\n }\n\n const nonWhitespace = text.slice(pos + 1).search(/\\S/)\n\n if (nonWhitespace !== -1) {\n pos += nonWhitespace + 1\n } else {\n pos = len\n result = result.trimEnd()\n }\n continue\n }\n\n // nothing matched => normal character\n result += c\n pos += 1\n }\n }\n\n if (typeof strings === 'string') strings = [strings] as unknown as TemplateStringsArray\n\n sub.forEach((it, idx) => {\n feed(strings[idx])\n\n if (typeof it === 'boolean' || !it) return\n\n if (typeof it === 'string' || typeof it === 'number') {\n result += it\n } else {\n // TextWithEntities or MessageEntity\n const text = it.text\n const innerEntities = 'raw' in it ? [it.raw] : it.entities\n\n const baseOffset = result.length\n result += text\n\n if (innerEntities) {\n for (const ent of innerEntities) {\n entities.push({ ...ent, offset: ent.offset + baseOffset })\n }\n }\n }\n })\n\n feed(strings[strings.length - 1])\n\n for (const [name, stack] of Object.entries(stacks)) {\n if (stack.length) {\n throw new Error(`Unterminated ${name} entity`)\n }\n }\n\n return {\n text: result,\n entities,\n }\n}\n\n// typedoc doesn't support this yet, so we'll have to do it manually\n// https://github.com/TypeStrong/typedoc/issues/2436\n\nexport const md: {\n /**\n * Tagged template based Markdown-to-entities parser function\n *\n * Additionally, `md` function has two static methods:\n * - `md.escape` - escape a string to be safely used in Markdown\n * (should not be needed in most cases, as `md` function itself handles all `string`s\n * passed to it automatically as plain text)\n * - `md.unparse` - add Markdown formatting to the text given the plain text and entities contained in it\n *\n * @example\n * ```typescript\n * const text = md`**${user.displayName}**`\n * ```\n */\n (\n strings: TemplateStringsArray,\n ...sub: (InputText | MessageEntity | boolean | number | undefined | null)[]\n ): TextWithEntities\n /**\n * A variant taking a plain JS string as input\n * and parsing it.\n *\n * Useful for cases when you already have a string\n * (e.g. from some server) and want to parse it.\n *\n * @example\n * ```typescript\n * const string = '**hello**'\n * const text = md(string)\n * ```\n */\n (string: string): TextWithEntities\n escape: typeof escape\n unparse: typeof unparse\n} = Object.assign(parse, {\n escape,\n unparse,\n})\n"]}
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
package/esm/index.d.ts ADDED
@@ -0,0 +1,46 @@
1
+ import type { InputText, MessageEntity, TextWithEntities } from '@mtcute/client';
2
+ /**
3
+ * Escape a string to be safely used in Markdown.
4
+ *
5
+ * > **Note**: this function is in most cases not needed, as `md` function
6
+ * > handles all `string`s passed to it automatically as plain text.
7
+ */
8
+ declare function escape(str: string): string;
9
+ /**
10
+ * Add Markdown formatting to the text given the plain text and entities contained in it.
11
+ */
12
+ declare function unparse(input: InputText): string;
13
+ export declare const md: {
14
+ /**
15
+ * Tagged template based Markdown-to-entities parser function
16
+ *
17
+ * Additionally, `md` function has two static methods:
18
+ * - `md.escape` - escape a string to be safely used in Markdown
19
+ * (should not be needed in most cases, as `md` function itself handles all `string`s
20
+ * passed to it automatically as plain text)
21
+ * - `md.unparse` - add Markdown formatting to the text given the plain text and entities contained in it
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * const text = md`**${user.displayName}**`
26
+ * ```
27
+ */
28
+ (strings: TemplateStringsArray, ...sub: (InputText | MessageEntity | boolean | number | undefined | null)[]): TextWithEntities;
29
+ /**
30
+ * A variant taking a plain JS string as input
31
+ * and parsing it.
32
+ *
33
+ * Useful for cases when you already have a string
34
+ * (e.g. from some server) and want to parse it.
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * const string = '**hello**'
39
+ * const text = md(string)
40
+ * ```
41
+ */
42
+ (string: string): TextWithEntities;
43
+ escape: typeof escape;
44
+ unparse: typeof unparse;
45
+ };
46
+ export {};
package/esm/index.js ADDED
@@ -0,0 +1,375 @@
1
+ import Long from 'long';
2
+ const MENTION_REGEX = /^tg:\/\/user\?id=(\d+)(?:&hash=(-?[0-9a-fA-F]+)(?:&|$)|&|$)/;
3
+ const EMOJI_REGEX = /^tg:\/\/emoji\?id=(-?\d+)/;
4
+ const TAG_BOLD = '**';
5
+ const TAG_ITALIC = '__';
6
+ const TAG_UNDERLINE = '--';
7
+ const TAG_STRIKE = '~~';
8
+ const TAG_SPOILER = '||';
9
+ const TAG_CODE = '`';
10
+ const TAG_PRE = '```';
11
+ const TO_BE_ESCAPED = /[*_\-~`[\\\]|]/g;
12
+ /**
13
+ * Escape a string to be safely used in Markdown.
14
+ *
15
+ * > **Note**: this function is in most cases not needed, as `md` function
16
+ * > handles all `string`s passed to it automatically as plain text.
17
+ */
18
+ function escape(str) {
19
+ return str.replace(TO_BE_ESCAPED, (s) => '\\' + s);
20
+ }
21
+ /**
22
+ * Add Markdown formatting to the text given the plain text and entities contained in it.
23
+ */
24
+ function unparse(input) {
25
+ if (typeof input === 'string')
26
+ return escape(input);
27
+ let text = input.text;
28
+ const entities = input.entities ?? [];
29
+ // keep track of positions of inserted escape symbols
30
+ const escaped = [];
31
+ text = text.replace(TO_BE_ESCAPED, (s, pos) => {
32
+ escaped.push(pos);
33
+ return '\\' + s;
34
+ });
35
+ const hasEscaped = escaped.length > 0;
36
+ const insert = [];
37
+ for (const entity of entities) {
38
+ const type = entity._;
39
+ let start = entity.offset;
40
+ let end = start + entity.length;
41
+ if (start > text.length)
42
+ continue;
43
+ if (start < 0)
44
+ start = 0;
45
+ if (end > text.length)
46
+ end = text.length;
47
+ if (hasEscaped) {
48
+ // determine number of escape chars since the beginning of the string
49
+ let escapedPos = 0;
50
+ while (escapedPos < escaped.length && escaped[escapedPos] < start) {
51
+ escapedPos += 1;
52
+ }
53
+ start += escapedPos;
54
+ while (escapedPos < escaped.length && escaped[escapedPos] <= end) {
55
+ escapedPos += 1;
56
+ }
57
+ end += escapedPos;
58
+ }
59
+ let startTag;
60
+ let endTag;
61
+ switch (type) {
62
+ case 'messageEntityBold':
63
+ startTag = endTag = TAG_BOLD;
64
+ break;
65
+ case 'messageEntityItalic':
66
+ startTag = endTag = TAG_ITALIC;
67
+ break;
68
+ case 'messageEntityUnderline':
69
+ startTag = endTag = TAG_UNDERLINE;
70
+ break;
71
+ case 'messageEntityStrike':
72
+ startTag = endTag = TAG_STRIKE;
73
+ break;
74
+ case 'messageEntitySpoiler':
75
+ startTag = endTag = TAG_SPOILER;
76
+ break;
77
+ case 'messageEntityCode':
78
+ startTag = endTag = TAG_CODE;
79
+ break;
80
+ case 'messageEntityPre':
81
+ startTag = TAG_PRE;
82
+ if (entity.language) {
83
+ startTag += entity.language;
84
+ }
85
+ startTag += '\n';
86
+ endTag = '\n' + TAG_PRE;
87
+ break;
88
+ case 'messageEntityTextUrl':
89
+ startTag = '[';
90
+ endTag = `](${entity.url})`;
91
+ break;
92
+ case 'messageEntityMentionName':
93
+ startTag = '[';
94
+ endTag = `](tg://user?id=${entity.userId})`;
95
+ break;
96
+ case 'messageEntityCustomEmoji':
97
+ startTag = '[';
98
+ endTag = `](tg://emoji?id=${entity.documentId.toString()})`;
99
+ break;
100
+ default:
101
+ continue;
102
+ }
103
+ insert.push([start, startTag]);
104
+ insert.push([end, endTag]);
105
+ }
106
+ // sort by offset desc
107
+ insert.sort((a, b) => b[0] - a[0]);
108
+ for (const [offset, tag] of insert) {
109
+ text = text.substr(0, offset) + tag + text.substr(offset);
110
+ }
111
+ return text;
112
+ }
113
+ function parse(strings, ...sub) {
114
+ const entities = [];
115
+ let result = '';
116
+ const stacks = {};
117
+ let insideCode = false;
118
+ let insidePre = false;
119
+ let insideLink = false;
120
+ function feed(text) {
121
+ const len = text.length;
122
+ let pos = 0;
123
+ while (pos < len) {
124
+ const c = text[pos];
125
+ if (c === '\\') {
126
+ result += text[pos + 1];
127
+ pos += 2;
128
+ continue;
129
+ }
130
+ if (insideCode) {
131
+ if (c === '`') {
132
+ // we can be certain that we're inside code
133
+ const ent = stacks.code.pop();
134
+ ent.length = result.length - ent.offset;
135
+ entities.push(ent);
136
+ insideCode = false;
137
+ pos += 1;
138
+ }
139
+ else {
140
+ pos += 1;
141
+ result += c;
142
+ }
143
+ continue;
144
+ }
145
+ if (insidePre) {
146
+ if (c === '`' || (c === '\n' && text[pos + 1] === '`')) {
147
+ if (c === '\n')
148
+ pos += 1;
149
+ if (text[pos + 1] === '`' && text[pos + 2] === '`') {
150
+ // we can be certain that we're inside pre
151
+ const ent = stacks.pre.pop();
152
+ ent.length = result.length - ent.offset;
153
+ entities.push(ent);
154
+ insidePre = false;
155
+ pos += 3;
156
+ continue;
157
+ // closed with single or double backtick
158
+ // i.e. not closed actually! this is totally valid md:
159
+ // ```javascript
160
+ // const a = ``;
161
+ // ```
162
+ // compensate that `pos` change we made earliers
163
+ }
164
+ else if (c === '\n') {
165
+ pos -= 1;
166
+ }
167
+ }
168
+ pos += 1;
169
+ result += c;
170
+ continue;
171
+ }
172
+ if (insideLink && c === ']') {
173
+ // we can be certain that we're inside link
174
+ const ent = stacks.link.pop();
175
+ if (text[pos + 1] !== '(') {
176
+ // [link text]
177
+ // ignore this, and add opening [
178
+ result = `${result.substr(0, ent.offset)}[${result.substr(ent.offset)}]`;
179
+ pos += 1;
180
+ insideLink = false;
181
+ continue;
182
+ }
183
+ pos += 2;
184
+ let url = '';
185
+ while (pos < text.length && text[pos] !== ')') {
186
+ url += text[pos++];
187
+ }
188
+ pos += 1; // )
189
+ if (pos > text.length) {
190
+ throw new Error('Malformed LINK entity, expected )');
191
+ }
192
+ if (url.length) {
193
+ ent.length = result.length - ent.offset;
194
+ let m = url.match(MENTION_REGEX);
195
+ if (m) {
196
+ const userId = parseInt(m[1]);
197
+ const accessHash = m[2];
198
+ if (accessHash) {
199
+ ent._ =
200
+ 'inputMessageEntityMentionName';
201
+ ent.userId = {
202
+ _: 'inputUser',
203
+ userId,
204
+ accessHash: Long.fromString(accessHash, false, 16),
205
+ };
206
+ }
207
+ else {
208
+ ent._ = 'messageEntityMentionName';
209
+ ent.userId = userId;
210
+ }
211
+ }
212
+ else if ((m = EMOJI_REGEX.exec(url))) {
213
+ ent._ = 'messageEntityCustomEmoji';
214
+ ent.documentId = Long.fromString(m[1]);
215
+ }
216
+ else {
217
+ if (url.match(/^\/\//))
218
+ url = 'http:' + url;
219
+ ent._ = 'messageEntityTextUrl';
220
+ ent.url = url;
221
+ }
222
+ entities.push(ent);
223
+ }
224
+ insideLink = false;
225
+ continue;
226
+ }
227
+ if (c === '[' && !insideLink) {
228
+ pos += 1;
229
+ insideLink = true;
230
+ if (!('link' in stacks))
231
+ stacks.link = [];
232
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
233
+ stacks.link.push({
234
+ offset: result.length,
235
+ length: 0,
236
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
237
+ }); // other fields are added after the second part
238
+ continue;
239
+ }
240
+ if (c === '`') {
241
+ const isPre = text[pos + 1] === '`' && text[pos + 2] === '`';
242
+ if (isPre) {
243
+ pos += 3;
244
+ let language = '';
245
+ while (pos < text.length && text[pos] !== '\n') {
246
+ language += text[pos++];
247
+ }
248
+ // newline
249
+ pos += 1;
250
+ if (pos > text.length) {
251
+ throw new Error('Malformed PRE entity, expected LF after ```');
252
+ }
253
+ if (!('pre' in stacks))
254
+ stacks.pre = [];
255
+ stacks.pre.push({
256
+ _: 'messageEntityPre',
257
+ offset: result.length,
258
+ length: 0,
259
+ language,
260
+ });
261
+ insidePre = true;
262
+ }
263
+ else {
264
+ pos += 1;
265
+ if (!('code' in stacks))
266
+ stacks.code = [];
267
+ stacks.code.push({
268
+ _: 'messageEntityCode',
269
+ offset: result.length,
270
+ length: 0,
271
+ });
272
+ insideCode = true;
273
+ }
274
+ continue;
275
+ }
276
+ if (c === text[pos + 1]) {
277
+ // maybe (?) start or end of an entity
278
+ let type = null;
279
+ switch (c) {
280
+ case '_':
281
+ type = 'Italic';
282
+ break;
283
+ case '*':
284
+ type = 'Bold';
285
+ break;
286
+ case '-':
287
+ type = 'Underline';
288
+ break;
289
+ case '~':
290
+ type = 'Strike';
291
+ break;
292
+ case '|':
293
+ type = 'Spoiler';
294
+ break;
295
+ }
296
+ if (type) {
297
+ if (!(type in stacks))
298
+ stacks[type] = [];
299
+ const isBegin = stacks[type].length === 0;
300
+ if (isBegin) {
301
+ stacks[type].push({
302
+ _: `messageEntity${type}`,
303
+ offset: result.length,
304
+ length: 0,
305
+ });
306
+ }
307
+ else {
308
+ // valid because isBegin is false
309
+ const ent = stacks[type].pop();
310
+ ent.length = result.length - ent.offset;
311
+ entities.push(ent);
312
+ }
313
+ pos += 2;
314
+ continue;
315
+ }
316
+ }
317
+ if (c === '\n') {
318
+ if (pos !== 0) {
319
+ result += '\n';
320
+ }
321
+ const nonWhitespace = text.slice(pos + 1).search(/\S/);
322
+ if (nonWhitespace !== -1) {
323
+ pos += nonWhitespace + 1;
324
+ }
325
+ else {
326
+ pos = len;
327
+ result = result.trimEnd();
328
+ }
329
+ continue;
330
+ }
331
+ // nothing matched => normal character
332
+ result += c;
333
+ pos += 1;
334
+ }
335
+ }
336
+ if (typeof strings === 'string')
337
+ strings = [strings];
338
+ sub.forEach((it, idx) => {
339
+ feed(strings[idx]);
340
+ if (typeof it === 'boolean' || !it)
341
+ return;
342
+ if (typeof it === 'string' || typeof it === 'number') {
343
+ result += it;
344
+ }
345
+ else {
346
+ // TextWithEntities or MessageEntity
347
+ const text = it.text;
348
+ const innerEntities = 'raw' in it ? [it.raw] : it.entities;
349
+ const baseOffset = result.length;
350
+ result += text;
351
+ if (innerEntities) {
352
+ for (const ent of innerEntities) {
353
+ entities.push({ ...ent, offset: ent.offset + baseOffset });
354
+ }
355
+ }
356
+ }
357
+ });
358
+ feed(strings[strings.length - 1]);
359
+ for (const [name, stack] of Object.entries(stacks)) {
360
+ if (stack.length) {
361
+ throw new Error(`Unterminated ${name} entity`);
362
+ }
363
+ }
364
+ return {
365
+ text: result,
366
+ entities,
367
+ };
368
+ }
369
+ // typedoc doesn't support this yet, so we'll have to do it manually
370
+ // https://github.com/TypeStrong/typedoc/issues/2436
371
+ export const md = Object.assign(parse, {
372
+ escape,
373
+ unparse,
374
+ });
375
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAA;AAIvB,MAAM,aAAa,GAAG,6DAA6D,CAAA;AACnF,MAAM,WAAW,GAAG,2BAA2B,CAAA;AAE/C,MAAM,QAAQ,GAAG,IAAI,CAAA;AACrB,MAAM,UAAU,GAAG,IAAI,CAAA;AACvB,MAAM,aAAa,GAAG,IAAI,CAAA;AAC1B,MAAM,UAAU,GAAG,IAAI,CAAA;AACvB,MAAM,WAAW,GAAG,IAAI,CAAA;AACxB,MAAM,QAAQ,GAAG,GAAG,CAAA;AACpB,MAAM,OAAO,GAAG,KAAK,CAAA;AAErB,MAAM,aAAa,GAAG,iBAAiB,CAAA;AAEvC;;;;;GAKG;AACH,SAAS,MAAM,CAAC,GAAW;IACvB,OAAO,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,CAAA;AACtD,CAAC;AAED;;GAEG;AACH,SAAS,OAAO,CAAC,KAAgB;IAC7B,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAA;IAEnD,IAAI,IAAI,GAAG,KAAK,CAAC,IAAI,CAAA;IACrB,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAA;IAErC,qDAAqD;IACrD,MAAM,OAAO,GAAa,EAAE,CAAA;IAC5B,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC,EAAE,GAAW,EAAE,EAAE;QAClD,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAEjB,OAAO,IAAI,GAAG,CAAC,CAAA;IACnB,CAAC,CAAC,CAAA;IACF,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAA;IAGrC,MAAM,MAAM,GAAkB,EAAE,CAAA;IAEhC,KAAK,MAAM,MAAM,IAAI,QAAQ,EAAE;QAC3B,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAA;QAErB,IAAI,KAAK,GAAG,MAAM,CAAC,MAAM,CAAA;QACzB,IAAI,GAAG,GAAG,KAAK,GAAG,MAAM,CAAC,MAAM,CAAA;QAE/B,IAAI,KAAK,GAAG,IAAI,CAAC,MAAM;YAAE,SAAQ;QACjC,IAAI,KAAK,GAAG,CAAC;YAAE,KAAK,GAAG,CAAC,CAAA;QACxB,IAAI,GAAG,GAAG,IAAI,CAAC,MAAM;YAAE,GAAG,GAAG,IAAI,CAAC,MAAM,CAAA;QAExC,IAAI,UAAU,EAAE;YACZ,qEAAqE;YACrE,IAAI,UAAU,GAAG,CAAC,CAAA;YAElB,OAAO,UAAU,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,KAAK,EAAE;gBAC/D,UAAU,IAAI,CAAC,CAAA;aAClB;YACD,KAAK,IAAI,UAAU,CAAA;YAEnB,OAAO,UAAU,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,GAAG,EAAE;gBAC9D,UAAU,IAAI,CAAC,CAAA;aAClB;YACD,GAAG,IAAI,UAAU,CAAA;SACpB;QAED,IAAI,QAAQ,CAAA;QACZ,IAAI,MAAc,CAAA;QAElB,QAAQ,IAAI,EAAE;YACV,KAAK,mBAAmB;gBACpB,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAA;gBAC5B,MAAK;YACT,KAAK,qBAAqB;gBACtB,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAA;gBAC9B,MAAK;YACT,KAAK,wBAAwB;gBACzB,QAAQ,GAAG,MAAM,GAAG,aAAa,CAAA;gBACjC,MAAK;YACT,KAAK,qBAAqB;gBACtB,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAA;gBAC9B,MAAK;YACT,KAAK,sBAAsB;gBACvB,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAA;gBAC/B,MAAK;YACT,KAAK,mBAAmB;gBACpB,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAA;gBAC5B,MAAK;YACT,KAAK,kBAAkB;gBACnB,QAAQ,GAAG,OAAO,CAAA;gBAElB,IAAI,MAAM,CAAC,QAAQ,EAAE;oBACjB,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAA;iBAC9B;gBAED,QAAQ,IAAI,IAAI,CAAA;gBAChB,MAAM,GAAG,IAAI,GAAG,OAAO,CAAA;gBACvB,MAAK;YACT,KAAK,sBAAsB;gBACvB,QAAQ,GAAG,GAAG,CAAA;gBACd,MAAM,GAAG,KAAK,MAAM,CAAC,GAAG,GAAG,CAAA;gBAC3B,MAAK;YACT,KAAK,0BAA0B;gBAC3B,QAAQ,GAAG,GAAG,CAAA;gBACd,MAAM,GAAG,kBAAkB,MAAM,CAAC,MAAM,GAAG,CAAA;gBAC3C,MAAK;YACT,KAAK,0BAA0B;gBAC3B,QAAQ,GAAG,GAAG,CAAA;gBACd,MAAM,GAAG,mBAAmB,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,GAAG,CAAA;gBAC3D,MAAK;YACT;gBACI,SAAQ;SACf;QAED,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAA;QAC9B,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAA;KAC7B;IAED,sBAAsB;IACtB,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAElC,KAAK,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,MAAM,EAAE;QAChC,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;KAC5D;IAED,OAAO,IAAI,CAAA;AACf,CAAC;AAED,SAAS,KAAK,CACV,OAAsC,EACtC,GAAG,GAAwE;IAE3E,MAAM,QAAQ,GAA2B,EAAE,CAAA;IAC3C,IAAI,MAAM,GAAG,EAAE,CAAA;IAEf,MAAM,MAAM,GAAuD,EAAE,CAAA;IAErE,IAAI,UAAU,GAAG,KAAK,CAAA;IACtB,IAAI,SAAS,GAAG,KAAK,CAAA;IACrB,IAAI,UAAU,GAAG,KAAK,CAAA;IAEtB,SAAS,IAAI,CAAC,IAAY;QACtB,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAA;QACvB,IAAI,GAAG,GAAG,CAAC,CAAA;QAEX,OAAO,GAAG,GAAG,GAAG,EAAE;YACd,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAA;YAEnB,IAAI,CAAC,KAAK,IAAI,EAAE;gBACZ,MAAM,IAAI,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAA;gBACvB,GAAG,IAAI,CAAC,CAAA;gBACR,SAAQ;aACX;YAED,IAAI,UAAU,EAAE;gBACZ,IAAI,CAAC,KAAK,GAAG,EAAE;oBACX,2CAA2C;oBAE3C,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAG,CAAA;oBAC9B,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAA;oBACvC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;oBAClB,UAAU,GAAG,KAAK,CAAA;oBAClB,GAAG,IAAI,CAAC,CAAA;iBACX;qBAAM;oBACH,GAAG,IAAI,CAAC,CAAA;oBACR,MAAM,IAAI,CAAC,CAAA;iBACd;gBACD,SAAQ;aACX;YAED,IAAI,SAAS,EAAE;gBACX,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,KAAK,IAAI,IAAI,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC,EAAE;oBACpD,IAAI,CAAC,KAAK,IAAI;wBAAE,GAAG,IAAI,CAAC,CAAA;oBAExB,IAAI,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,GAAG,IAAI,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE;wBAChD,0CAA0C;wBAE1C,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,EAAG,CAAA;wBAC7B,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAA;wBACvC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;wBAClB,SAAS,GAAG,KAAK,CAAA;wBACjB,GAAG,IAAI,CAAC,CAAA;wBACR,SAAQ;wBAER,wCAAwC;wBACxC,sDAAsD;wBACtD,gBAAgB;wBAChB,gBAAgB;wBAChB,MAAM;wBACN,gDAAgD;qBACnD;yBAAM,IAAI,CAAC,KAAK,IAAI,EAAE;wBACnB,GAAG,IAAI,CAAC,CAAA;qBACX;iBACJ;gBAED,GAAG,IAAI,CAAC,CAAA;gBACR,MAAM,IAAI,CAAC,CAAA;gBACX,SAAQ;aACX;YAED,IAAI,UAAU,IAAI,CAAC,KAAK,GAAG,EAAE;gBACzB,2CAA2C;gBAE3C,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAG,CAAA;gBAE9B,IAAI,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE;oBACvB,cAAc;oBACd,iCAAiC;oBACjC,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAA;oBACxE,GAAG,IAAI,CAAC,CAAA;oBACR,UAAU,GAAG,KAAK,CAAA;oBAClB,SAAQ;iBACX;gBAED,GAAG,IAAI,CAAC,CAAA;gBACR,IAAI,GAAG,GAAG,EAAE,CAAA;gBAEZ,OAAO,GAAG,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,EAAE;oBAC3C,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;iBACrB;gBAED,GAAG,IAAI,CAAC,CAAA,CAAC,IAAI;gBAEb,IAAI,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE;oBACnB,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAA;iBACvD;gBAED,IAAI,GAAG,CAAC,MAAM,EAAE;oBACZ,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAA;oBAEvC,IAAI,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,aAAa,CAAC,CAAA;oBAEhC,IAAI,CAAC,EAAE;wBACH,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;wBAC7B,MAAM,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;wBAEvB,IAAI,UAAU,EAAE;4BACX,GAAuD,CAAC,CAAC;gCACtD,+BAA+B,CAClC;4BAAC,GAAuD,CAAC,MAAM,GAAG;gCAC/D,CAAC,EAAE,WAAW;gCACd,MAAM;gCACN,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,KAAK,EAAE,EAAE,CAAC;6BACrD,CAAA;yBACJ;6BAAM;4BACF,GAAkD,CAAC,CAAC,GAAG,0BAA0B,CACjF;4BAAC,GAAkD,CAAC,MAAM,GAAG,MAAM,CAAA;yBACvE;qBACJ;yBAAM,IAAI,CAAC,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE;wBACnC,GAAkD,CAAC,CAAC,GAAG,0BAA0B,CACjF;wBAAC,GAAkD,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;qBAC1F;yBAAM;wBACH,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC;4BAAE,GAAG,GAAG,OAAO,GAAG,GAAG,CAC1C;wBAAC,GAA8C,CAAC,CAAC,GAAG,sBAAsB,CAC1E;wBAAC,GAA8C,CAAC,GAAG,GAAG,GAAG,CAAA;qBAC7D;oBACD,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;iBACrB;gBAED,UAAU,GAAG,KAAK,CAAA;gBAClB,SAAQ;aACX;YAED,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,UAAU,EAAE;gBAC1B,GAAG,IAAI,CAAC,CAAA;gBACR,UAAU,GAAG,IAAI,CAAA;gBACjB,IAAI,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC;oBAAE,MAAM,CAAC,IAAI,GAAG,EAAE,CAAA;gBACzC,iEAAiE;gBACjE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;oBACb,MAAM,EAAE,MAAM,CAAC,MAAM;oBACrB,MAAM,EAAE,CAAC;oBACT,8DAA8D;iBAC1D,CAAC,CAAA,CAAC,+CAA+C;gBACzD,SAAQ;aACX;YAED,IAAI,CAAC,KAAK,GAAG,EAAE;gBACX,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,GAAG,IAAI,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,GAAG,CAAA;gBAE5D,IAAI,KAAK,EAAE;oBACP,GAAG,IAAI,CAAC,CAAA;oBACR,IAAI,QAAQ,GAAG,EAAE,CAAA;oBAEjB,OAAO,GAAG,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE;wBAC5C,QAAQ,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;qBAC1B;oBAED,UAAU;oBACV,GAAG,IAAI,CAAC,CAAA;oBAER,IAAI,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE;wBACnB,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAA;qBACjE;oBAED,IAAI,CAAC,CAAC,KAAK,IAAI,MAAM,CAAC;wBAAE,MAAM,CAAC,GAAG,GAAG,EAAE,CAAA;oBACvC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;wBACZ,CAAC,EAAE,kBAAkB;wBACrB,MAAM,EAAE,MAAM,CAAC,MAAM;wBACrB,MAAM,EAAE,CAAC;wBACT,QAAQ;qBACX,CAAC,CAAA;oBACF,SAAS,GAAG,IAAI,CAAA;iBACnB;qBAAM;oBACH,GAAG,IAAI,CAAC,CAAA;oBACR,IAAI,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC;wBAAE,MAAM,CAAC,IAAI,GAAG,EAAE,CAAA;oBACzC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;wBACb,CAAC,EAAE,mBAAmB;wBACtB,MAAM,EAAE,MAAM,CAAC,MAAM;wBACrB,MAAM,EAAE,CAAC;qBACZ,CAAC,CAAA;oBACF,UAAU,GAAG,IAAI,CAAA;iBACpB;gBAED,SAAQ;aACX;YAED,IAAI,CAAC,KAAK,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE;gBACrB,sCAAsC;gBACtC,IAAI,IAAI,GAAkE,IAAI,CAAA;gBAE9E,QAAQ,CAAC,EAAE;oBACP,KAAK,GAAG;wBACJ,IAAI,GAAG,QAAQ,CAAA;wBACf,MAAK;oBACT,KAAK,GAAG;wBACJ,IAAI,GAAG,MAAM,CAAA;wBACb,MAAK;oBACT,KAAK,GAAG;wBACJ,IAAI,GAAG,WAAW,CAAA;wBAClB,MAAK;oBACT,KAAK,GAAG;wBACJ,IAAI,GAAG,QAAQ,CAAA;wBACf,MAAK;oBACT,KAAK,GAAG;wBACJ,IAAI,GAAG,SAAS,CAAA;wBAChB,MAAK;iBACZ;gBAED,IAAI,IAAI,EAAE;oBACN,IAAI,CAAC,CAAC,IAAI,IAAI,MAAM,CAAC;wBAAE,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAA;oBACxC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAA;oBAEzC,IAAI,OAAO,EAAE;wBACT,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC;4BACd,CAAC,EAAE,gBAAgB,IAAI,EAAE;4BACzB,MAAM,EAAE,MAAM,CAAC,MAAM;4BACrB,MAAM,EAAE,CAAC;yBACZ,CAAC,CAAA;qBACL;yBAAM;wBACH,iCAAiC;wBAEjC,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,EAAG,CAAA;wBAC/B,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAA;wBACvC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;qBACrB;oBAED,GAAG,IAAI,CAAC,CAAA;oBACR,SAAQ;iBACX;aACJ;YAED,IAAI,CAAC,KAAK,IAAI,EAAE;gBACZ,IAAI,GAAG,KAAK,CAAC,EAAE;oBACX,MAAM,IAAI,IAAI,CAAA;iBACjB;gBAED,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;gBAEtD,IAAI,aAAa,KAAK,CAAC,CAAC,EAAE;oBACtB,GAAG,IAAI,aAAa,GAAG,CAAC,CAAA;iBAC3B;qBAAM;oBACH,GAAG,GAAG,GAAG,CAAA;oBACT,MAAM,GAAG,MAAM,CAAC,OAAO,EAAE,CAAA;iBAC5B;gBACD,SAAQ;aACX;YAED,sCAAsC;YACtC,MAAM,IAAI,CAAC,CAAA;YACX,GAAG,IAAI,CAAC,CAAA;SACX;IACL,CAAC;IAED,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC,OAAO,CAAoC,CAAA;IAEvF,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE;QACpB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;QAElB,IAAI,OAAO,EAAE,KAAK,SAAS,IAAI,CAAC,EAAE;YAAE,OAAM;QAE1C,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,OAAO,EAAE,KAAK,QAAQ,EAAE;YAClD,MAAM,IAAI,EAAE,CAAA;SACf;aAAM;YACH,oCAAoC;YACpC,MAAM,IAAI,GAAG,EAAE,CAAC,IAAI,CAAA;YACpB,MAAM,aAAa,GAAG,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAA;YAE1D,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAA;YAChC,MAAM,IAAI,IAAI,CAAA;YAEd,IAAI,aAAa,EAAE;gBACf,KAAK,MAAM,GAAG,IAAI,aAAa,EAAE;oBAC7B,QAAQ,CAAC,IAAI,CAAC,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,UAAU,EAAE,CAAC,CAAA;iBAC7D;aACJ;SACJ;IACL,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAA;IAEjC,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;QAChD,IAAI,KAAK,CAAC,MAAM,EAAE;YACd,MAAM,IAAI,KAAK,CAAC,gBAAgB,IAAI,SAAS,CAAC,CAAA;SACjD;KACJ;IAED,OAAO;QACH,IAAI,EAAE,MAAM;QACZ,QAAQ;KACX,CAAA;AACL,CAAC;AAED,oEAAoE;AACpE,oDAAoD;AAEpD,MAAM,CAAC,MAAM,EAAE,GAmCX,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE;IACrB,MAAM;IACN,OAAO;CACV,CAAC,CAAA","sourcesContent":["import Long from 'long'\n\nimport type { InputText, MessageEntity, TextWithEntities, tl } from '@mtcute/client'\n\nconst MENTION_REGEX = /^tg:\\/\\/user\\?id=(\\d+)(?:&hash=(-?[0-9a-fA-F]+)(?:&|$)|&|$)/\nconst EMOJI_REGEX = /^tg:\\/\\/emoji\\?id=(-?\\d+)/\n\nconst TAG_BOLD = '**'\nconst TAG_ITALIC = '__'\nconst TAG_UNDERLINE = '--'\nconst TAG_STRIKE = '~~'\nconst TAG_SPOILER = '||'\nconst TAG_CODE = '`'\nconst TAG_PRE = '```'\n\nconst TO_BE_ESCAPED = /[*_\\-~`[\\\\\\]|]/g\n\n/**\n * Escape a string to be safely used in Markdown.\n *\n * > **Note**: this function is in most cases not needed, as `md` function\n * > handles all `string`s passed to it automatically as plain text.\n */\nfunction escape(str: string): string {\n return str.replace(TO_BE_ESCAPED, (s) => '\\\\' + s)\n}\n\n/**\n * Add Markdown formatting to the text given the plain text and entities contained in it.\n */\nfunction unparse(input: InputText): string {\n if (typeof input === 'string') return escape(input)\n\n let text = input.text\n const entities = input.entities ?? []\n\n // keep track of positions of inserted escape symbols\n const escaped: number[] = []\n text = text.replace(TO_BE_ESCAPED, (s, pos: number) => {\n escaped.push(pos)\n\n return '\\\\' + s\n })\n const hasEscaped = escaped.length > 0\n\n type InsertLater = [number, string]\n const insert: InsertLater[] = []\n\n for (const entity of entities) {\n const type = entity._\n\n let start = entity.offset\n let end = start + entity.length\n\n if (start > text.length) continue\n if (start < 0) start = 0\n if (end > text.length) end = text.length\n\n if (hasEscaped) {\n // determine number of escape chars since the beginning of the string\n let escapedPos = 0\n\n while (escapedPos < escaped.length && escaped[escapedPos] < start) {\n escapedPos += 1\n }\n start += escapedPos\n\n while (escapedPos < escaped.length && escaped[escapedPos] <= end) {\n escapedPos += 1\n }\n end += escapedPos\n }\n\n let startTag\n let endTag: string\n\n switch (type) {\n case 'messageEntityBold':\n startTag = endTag = TAG_BOLD\n break\n case 'messageEntityItalic':\n startTag = endTag = TAG_ITALIC\n break\n case 'messageEntityUnderline':\n startTag = endTag = TAG_UNDERLINE\n break\n case 'messageEntityStrike':\n startTag = endTag = TAG_STRIKE\n break\n case 'messageEntitySpoiler':\n startTag = endTag = TAG_SPOILER\n break\n case 'messageEntityCode':\n startTag = endTag = TAG_CODE\n break\n case 'messageEntityPre':\n startTag = TAG_PRE\n\n if (entity.language) {\n startTag += entity.language\n }\n\n startTag += '\\n'\n endTag = '\\n' + TAG_PRE\n break\n case 'messageEntityTextUrl':\n startTag = '['\n endTag = `](${entity.url})`\n break\n case 'messageEntityMentionName':\n startTag = '['\n endTag = `](tg://user?id=${entity.userId})`\n break\n case 'messageEntityCustomEmoji':\n startTag = '['\n endTag = `](tg://emoji?id=${entity.documentId.toString()})`\n break\n default:\n continue\n }\n\n insert.push([start, startTag])\n insert.push([end, endTag])\n }\n\n // sort by offset desc\n insert.sort((a, b) => b[0] - a[0])\n\n for (const [offset, tag] of insert) {\n text = text.substr(0, offset) + tag + text.substr(offset)\n }\n\n return text\n}\n\nfunction parse(\n strings: TemplateStringsArray | string,\n ...sub: (InputText | MessageEntity | boolean | number | undefined | null)[]\n): TextWithEntities {\n const entities: tl.TypeMessageEntity[] = []\n let result = ''\n\n const stacks: Record<string, tl.Mutable<tl.TypeMessageEntity>[]> = {}\n\n let insideCode = false\n let insidePre = false\n let insideLink = false\n\n function feed(text: string) {\n const len = text.length\n let pos = 0\n\n while (pos < len) {\n const c = text[pos]\n\n if (c === '\\\\') {\n result += text[pos + 1]\n pos += 2\n continue\n }\n\n if (insideCode) {\n if (c === '`') {\n // we can be certain that we're inside code\n\n const ent = stacks.code.pop()!\n ent.length = result.length - ent.offset\n entities.push(ent)\n insideCode = false\n pos += 1\n } else {\n pos += 1\n result += c\n }\n continue\n }\n\n if (insidePre) {\n if (c === '`' || (c === '\\n' && text[pos + 1] === '`')) {\n if (c === '\\n') pos += 1\n\n if (text[pos + 1] === '`' && text[pos + 2] === '`') {\n // we can be certain that we're inside pre\n\n const ent = stacks.pre.pop()!\n ent.length = result.length - ent.offset\n entities.push(ent)\n insidePre = false\n pos += 3\n continue\n\n // closed with single or double backtick\n // i.e. not closed actually! this is totally valid md:\n // ```javascript\n // const a = ``;\n // ```\n // compensate that `pos` change we made earliers\n } else if (c === '\\n') {\n pos -= 1\n }\n }\n\n pos += 1\n result += c\n continue\n }\n\n if (insideLink && c === ']') {\n // we can be certain that we're inside link\n\n const ent = stacks.link.pop()!\n\n if (text[pos + 1] !== '(') {\n // [link text]\n // ignore this, and add opening [\n result = `${result.substr(0, ent.offset)}[${result.substr(ent.offset)}]`\n pos += 1\n insideLink = false\n continue\n }\n\n pos += 2\n let url = ''\n\n while (pos < text.length && text[pos] !== ')') {\n url += text[pos++]\n }\n\n pos += 1 // )\n\n if (pos > text.length) {\n throw new Error('Malformed LINK entity, expected )')\n }\n\n if (url.length) {\n ent.length = result.length - ent.offset\n\n let m = url.match(MENTION_REGEX)\n\n if (m) {\n const userId = parseInt(m[1])\n const accessHash = m[2]\n\n if (accessHash) {\n (ent as tl.Mutable<tl.RawInputMessageEntityMentionName>)._ =\n 'inputMessageEntityMentionName'\n ;(ent as tl.Mutable<tl.RawInputMessageEntityMentionName>).userId = {\n _: 'inputUser',\n userId,\n accessHash: Long.fromString(accessHash, false, 16),\n }\n } else {\n (ent as tl.Mutable<tl.RawMessageEntityMentionName>)._ = 'messageEntityMentionName'\n ;(ent as tl.Mutable<tl.RawMessageEntityMentionName>).userId = userId\n }\n } else if ((m = EMOJI_REGEX.exec(url))) {\n (ent as tl.Mutable<tl.RawMessageEntityCustomEmoji>)._ = 'messageEntityCustomEmoji'\n ;(ent as tl.Mutable<tl.RawMessageEntityCustomEmoji>).documentId = Long.fromString(m[1])\n } else {\n if (url.match(/^\\/\\//)) url = 'http:' + url\n ;(ent as tl.Mutable<tl.RawMessageEntityTextUrl>)._ = 'messageEntityTextUrl'\n ;(ent as tl.Mutable<tl.RawMessageEntityTextUrl>).url = url\n }\n entities.push(ent)\n }\n\n insideLink = false\n continue\n }\n\n if (c === '[' && !insideLink) {\n pos += 1\n insideLink = true\n if (!('link' in stacks)) stacks.link = []\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n stacks.link.push({\n offset: result.length,\n length: 0,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } as any) // other fields are added after the second part\n continue\n }\n\n if (c === '`') {\n const isPre = text[pos + 1] === '`' && text[pos + 2] === '`'\n\n if (isPre) {\n pos += 3\n let language = ''\n\n while (pos < text.length && text[pos] !== '\\n') {\n language += text[pos++]\n }\n\n // newline\n pos += 1\n\n if (pos > text.length) {\n throw new Error('Malformed PRE entity, expected LF after ```')\n }\n\n if (!('pre' in stacks)) stacks.pre = []\n stacks.pre.push({\n _: 'messageEntityPre',\n offset: result.length,\n length: 0,\n language,\n })\n insidePre = true\n } else {\n pos += 1\n if (!('code' in stacks)) stacks.code = []\n stacks.code.push({\n _: 'messageEntityCode',\n offset: result.length,\n length: 0,\n })\n insideCode = true\n }\n\n continue\n }\n\n if (c === text[pos + 1]) {\n // maybe (?) start or end of an entity\n let type: 'Italic' | 'Bold' | 'Underline' | 'Strike' | 'Spoiler' | null = null\n\n switch (c) {\n case '_':\n type = 'Italic'\n break\n case '*':\n type = 'Bold'\n break\n case '-':\n type = 'Underline'\n break\n case '~':\n type = 'Strike'\n break\n case '|':\n type = 'Spoiler'\n break\n }\n\n if (type) {\n if (!(type in stacks)) stacks[type] = []\n const isBegin = stacks[type].length === 0\n\n if (isBegin) {\n stacks[type].push({\n _: `messageEntity${type}`,\n offset: result.length,\n length: 0,\n })\n } else {\n // valid because isBegin is false\n\n const ent = stacks[type].pop()!\n ent.length = result.length - ent.offset\n entities.push(ent)\n }\n\n pos += 2\n continue\n }\n }\n\n if (c === '\\n') {\n if (pos !== 0) {\n result += '\\n'\n }\n\n const nonWhitespace = text.slice(pos + 1).search(/\\S/)\n\n if (nonWhitespace !== -1) {\n pos += nonWhitespace + 1\n } else {\n pos = len\n result = result.trimEnd()\n }\n continue\n }\n\n // nothing matched => normal character\n result += c\n pos += 1\n }\n }\n\n if (typeof strings === 'string') strings = [strings] as unknown as TemplateStringsArray\n\n sub.forEach((it, idx) => {\n feed(strings[idx])\n\n if (typeof it === 'boolean' || !it) return\n\n if (typeof it === 'string' || typeof it === 'number') {\n result += it\n } else {\n // TextWithEntities or MessageEntity\n const text = it.text\n const innerEntities = 'raw' in it ? [it.raw] : it.entities\n\n const baseOffset = result.length\n result += text\n\n if (innerEntities) {\n for (const ent of innerEntities) {\n entities.push({ ...ent, offset: ent.offset + baseOffset })\n }\n }\n }\n })\n\n feed(strings[strings.length - 1])\n\n for (const [name, stack] of Object.entries(stacks)) {\n if (stack.length) {\n throw new Error(`Unterminated ${name} entity`)\n }\n }\n\n return {\n text: result,\n entities,\n }\n}\n\n// typedoc doesn't support this yet, so we'll have to do it manually\n// https://github.com/TypeStrong/typedoc/issues/2436\n\nexport const md: {\n /**\n * Tagged template based Markdown-to-entities parser function\n *\n * Additionally, `md` function has two static methods:\n * - `md.escape` - escape a string to be safely used in Markdown\n * (should not be needed in most cases, as `md` function itself handles all `string`s\n * passed to it automatically as plain text)\n * - `md.unparse` - add Markdown formatting to the text given the plain text and entities contained in it\n *\n * @example\n * ```typescript\n * const text = md`**${user.displayName}**`\n * ```\n */\n (\n strings: TemplateStringsArray,\n ...sub: (InputText | MessageEntity | boolean | number | undefined | null)[]\n ): TextWithEntities\n /**\n * A variant taking a plain JS string as input\n * and parsing it.\n *\n * Useful for cases when you already have a string\n * (e.g. from some server) and want to parse it.\n *\n * @example\n * ```typescript\n * const string = '**hello**'\n * const text = md(string)\n * ```\n */\n (string: string): TextWithEntities\n escape: typeof escape\n unparse: typeof unparse\n} = Object.assign(parse, {\n escape,\n unparse,\n})\n"]}
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@mtcute/markdown-parser",
3
+ "version": "0.1.0",
4
+ "description": "Markdown entities parser for mtcute",
5
+ "author": "Alina Sireneva <alina@tei.su>",
6
+ "license": "MIT",
7
+ "main": "cjs/index.js",
8
+ "type": "module",
9
+ "scripts": {},
10
+ "dependencies": {
11
+ "long": "5.2.3"
12
+ },
13
+ "module": "esm/index.js",
14
+ "exports": {
15
+ ".": {
16
+ "import": "./esm/index.js",
17
+ "require": "./cjs/index.js"
18
+ }
19
+ }
20
+ }