tellegram 1.1.6 → 1.1.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/README.md CHANGED
@@ -86,6 +86,11 @@ You can also add unsupported tags strategy as a second argument, which can be on
86
86
  - `remove` - remove unsupported tags
87
87
  - `keep` - ignore unsupported tags
88
88
 
89
+ `convert` also accepts a third argument for feature options:
90
+
91
+ - `table: 'list'` - convert GFM tables into MarkdownV2-safe hierarchical lists (default)
92
+ - `table: 'unsupported'` - treat tables as unsupported and apply the second-argument strategy
93
+
89
94
  ```js
90
95
  import { convert } from 'tellegram';
91
96
  const markdown = `
@@ -111,4 +116,43 @@ convert(markdown, 'remove');
111
116
  */
112
117
  ```
113
118
 
119
+ ### Convert tables to list
120
+
121
+ Telegram does not support Markdown tables. By default, teLLegraM converts a
122
+ table into a vertical hierarchical list. In this mode, list markers and
123
+ placeholder `-` are escaped to stay safe for Telegram MarkdownV2.
124
+
125
+ ```js
126
+ import { convert } from 'tellegram';
127
+
128
+ const markdown = `
129
+ | Name | Role | Score |
130
+ | ----- | ----- | ----- |
131
+ | Alice | Admin | 95 |
132
+ | Bob | User | 88 |
133
+ `;
134
+
135
+ convert(markdown, 'escape');
136
+ /*
137
+ \- Name: Alice
138
+ \- Role: Admin
139
+ \- Score: 95
140
+
141
+ \- Name: Bob
142
+ \- Role: User
143
+ \- Score: 88
144
+ */
145
+ ```
146
+
147
+ If you want the previous behavior (table treated as unsupported), use
148
+ `table: 'unsupported'` with the strategy you need:
149
+
150
+ ```js
151
+ convert(markdown, 'escape', { table: 'unsupported' });
152
+ // => table is kept as escaped Markdown table text
153
+
154
+ convert(markdown, 'remove', { table: 'unsupported' });
155
+ // => ''
156
+ ```
157
+
114
158
  [MIT Licence](LICENSE)
package/lib/convert.mjs CHANGED
@@ -7,10 +7,14 @@ import unified from 'unified';
7
7
  import { collectDefinitions, removeDefinitions } from './definitions.mjs';
8
8
  import createTellegramOptions from './tellegram.mjs';
9
9
 
10
- export default (markdown, unsupportedTagsStrategy = 'escape') => {
10
+ export default (markdown, unsupportedTagsStrategy = 'escape', options = {}) => {
11
11
  const definitions = {};
12
12
 
13
- const tellegramOptions = createTellegramOptions(definitions, unsupportedTagsStrategy);
13
+ const tellegramOptions = createTellegramOptions(
14
+ definitions,
15
+ unsupportedTagsStrategy,
16
+ options,
17
+ );
14
18
 
15
19
  return unified()
16
20
  .use(parse)
@@ -0,0 +1,66 @@
1
+ import phrasing from 'mdast-util-to-markdown/lib/util/container-phrasing.js';
2
+
3
+ import { escapeSymbols } from './utils.mjs';
4
+
5
+ const EMPTY_VALUE = escapeSymbols('-');
6
+ const FALLBACK_HEADER = escapeSymbols('-');
7
+
8
+ const normalizeCell = (node, context) => {
9
+ if (!node) {
10
+ return '';
11
+ }
12
+
13
+ const value = phrasing(node, context, { before: '', after: '' });
14
+ return value.replace(/\n/g, ' ').trim();
15
+ };
16
+
17
+ const normalizeHeader = (cell, index, context) => {
18
+ const value = normalizeCell(cell, context);
19
+ return value || `${FALLBACK_HEADER}`;
20
+ };
21
+
22
+ const normalizeValue = (cell, context) => {
23
+ const value = normalizeCell(cell, context);
24
+ return value || EMPTY_VALUE;
25
+ };
26
+
27
+ const pairToLine = (pair, isNested) => {
28
+ const prefix = isNested ? ' \\- ' : '\\- ';
29
+ return `${prefix}${pair.header}: ${pair.value}`;
30
+ };
31
+
32
+ export default (node, context) => {
33
+ if (!node.children.length) {
34
+ return '';
35
+ }
36
+
37
+ const [headerRow, ...rows] = node.children;
38
+ if (!headerRow || !rows.length) {
39
+ return '';
40
+ }
41
+
42
+ const headers = headerRow.children.map((cell, index) =>
43
+ normalizeHeader(cell, index, context));
44
+
45
+ const listRows = rows
46
+ .map((row, rowIndex) => {
47
+ const columnCount = Math.max(headers.length, row.children.length);
48
+ const pairs = Array.from({ length: columnCount }, (_, index) => {
49
+ const header = headers[index] || FALLBACK_HEADER;
50
+ const value = normalizeValue(row.children[index], context);
51
+ return { header, value };
52
+ });
53
+
54
+ if (!pairs.length) {
55
+ return `\\- Item ${rowIndex + 1}: ${EMPTY_VALUE}`;
56
+ }
57
+
58
+ const [first, ...rest] = pairs;
59
+ return [
60
+ pairToLine(first, false),
61
+ ...rest.map(pair => pairToLine(pair, true)),
62
+ ].join('\n');
63
+ });
64
+
65
+ return listRows.join('\n\n');
66
+ };
package/lib/tellegram.mjs CHANGED
@@ -3,6 +3,7 @@ import phrasing from 'mdast-util-to-markdown/lib/util/container-phrasing.js';
3
3
  import { toMarkdown as gfmTableToMarkdown } from 'mdast-util-gfm-table';
4
4
 
5
5
  import { wrap, isURL, escapeSymbols, processUnsupportedTags } from './utils.mjs';
6
+ import tableToList from './table-to-list.mjs';
6
7
 
7
8
  /**
8
9
  * Creates custom `mdast-util-to-markdown` handlers that tailor the output for
@@ -13,7 +14,7 @@ import { wrap, isURL, escapeSymbols, processUnsupportedTags } from './utils.mjs'
13
14
  *
14
15
  * @returns {import('mdast-util-to-markdown').Handlers}
15
16
  */
16
- const createHandlers = (definitions, unsupportedTagsStrategy) => ({
17
+ const createHandlers = (definitions, unsupportedTagsStrategy, options) => ({
17
18
  heading: (node, _parent, context) => {
18
19
  // make headers to be just *strong*
19
20
  const marker = '*';
@@ -134,8 +135,18 @@ const createHandlers = (definitions, unsupportedTagsStrategy) => ({
134
135
  processUnsupportedTags(defaultHandlers.blockquote(node, _parent, context), unsupportedTagsStrategy),
135
136
  html: (node, _parent, context) =>
136
137
  processUnsupportedTags(defaultHandlers.html(node, _parent, context), unsupportedTagsStrategy),
137
- table: (node, _parent, context) =>
138
- processUnsupportedTags(gfmTableToMarkdown().handlers.table(node, _parent, context), unsupportedTagsStrategy),
138
+ table: (node, _parent, context) => {
139
+ const tableMode = options.table || 'list';
140
+
141
+ if (tableMode === 'list') {
142
+ return tableToList(node, context);
143
+ }
144
+
145
+ return processUnsupportedTags(
146
+ gfmTableToMarkdown().handlers.table(node, _parent, context),
147
+ unsupportedTagsStrategy,
148
+ );
149
+ },
139
150
  thematicBreak: () =>
140
151
  processUnsupportedTags('---', unsupportedTagsStrategy),
141
152
  });
@@ -149,10 +160,10 @@ const createHandlers = (definitions, unsupportedTagsStrategy) => ({
149
160
  *
150
161
  * @returns {import('remark-stringify').RemarkStringifyOptions}
151
162
  */
152
- const createOptions = (definitions, unsupportedTagsStrategy) => ({
163
+ const createOptions = (definitions, unsupportedTagsStrategy, options = {}) => ({
153
164
  bullet: '*',
154
165
  tightDefinitions: true,
155
- handlers: createHandlers(definitions, unsupportedTagsStrategy),
166
+ handlers: createHandlers(definitions, unsupportedTagsStrategy, options),
156
167
  });
157
168
 
158
169
  export default createOptions;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tellegram",
3
- "version": "1.1.6",
3
+ "version": "1.1.8",
4
4
  "description": "Convert LLM-generated markdown into Telegram-specific markdown (MarkdownV2)",
5
5
  "type": "module",
6
6
  "main": "index.mjs",
package/types/index.d.ts CHANGED
@@ -1,12 +1,23 @@
1
1
  type UnsupportedTagsStrategy = 'escape' | 'remove' | 'keep'
2
+ type TableStrategy = 'list' | 'unsupported'
3
+
4
+ interface ConvertOptions {
5
+ /** Table conversion mode. Default: 'list' */
6
+ table?: TableStrategy
7
+ }
2
8
 
3
9
  declare module 'tellegram' {
4
10
  /**
5
11
  * Converts markdown to Telegram's format.
6
12
  * @param markdown The markdown to convert.
7
13
  * @param unsupportedTagsStrategy The strategy to use for unsupported tags.
14
+ * @param options Additional conversion options.
8
15
  */
9
- export function convert(markdown: string, unsupportedTagsStrategy?: UnsupportedTagsStrategy): string;
16
+ export function convert(
17
+ markdown: string,
18
+ unsupportedTagsStrategy?: UnsupportedTagsStrategy,
19
+ options?: ConvertOptions
20
+ ): string;
10
21
 
11
22
  /**
12
23
  * Paginates text (placeholder).
@@ -16,4 +27,4 @@ declare module 'tellegram' {
16
27
 
17
28
  const defaultExport: typeof convert;
18
29
  export default defaultExport;
19
- }
30
+ }