|
1 | 1 | /**
|
2 |
| - * @typedef {import('mdast').AlignType} AlignType |
3 |
| - * @typedef {import('mdast').Table} Table |
4 |
| - * @typedef {import('mdast').TableRow} TableRow |
5 |
| - * @typedef {import('mdast').TableCell} TableCell |
6 |
| - * @typedef {import('mdast').InlineCode} InlineCode |
7 |
| - * @typedef {import('markdown-table').MarkdownTableOptions} MarkdownTableOptions |
8 |
| - * @typedef {import('mdast-util-from-markdown').Extension} FromMarkdownExtension |
9 |
| - * @typedef {import('mdast-util-from-markdown').Handle} FromMarkdownHandle |
10 |
| - * @typedef {import('mdast-util-to-markdown').Options} ToMarkdownExtension |
11 |
| - * @typedef {import('mdast-util-to-markdown').Handle} ToMarkdownHandle |
12 |
| - * @typedef {import('mdast-util-to-markdown').Context} ToMarkdownContext |
13 |
| - * @typedef {import('mdast-util-to-markdown').SafeOptions} SafeOptions |
14 |
| - * |
15 |
| - * @typedef Options |
16 |
| - * @property {boolean} [tableCellPadding=true] |
17 |
| - * @property {boolean} [tablePipeAlign=true] |
18 |
| - * @property {MarkdownTableOptions['stringLength']} [stringLength] |
| 2 | + * @typedef {import('./lib/index.js').Options} Options |
19 | 3 | */
|
20 | 4 |
|
21 |
| -import {containerPhrasing} from 'mdast-util-to-markdown/lib/util/container-phrasing.js' |
22 |
| -import {inlineCode} from 'mdast-util-to-markdown/lib/handle/inline-code.js' |
23 |
| -import {markdownTable} from 'markdown-table' |
24 |
| - |
25 |
| -/** @type {FromMarkdownExtension} */ |
26 |
| -export const gfmTableFromMarkdown = { |
27 |
| - enter: { |
28 |
| - table: enterTable, |
29 |
| - tableData: enterCell, |
30 |
| - tableHeader: enterCell, |
31 |
| - tableRow: enterRow |
32 |
| - }, |
33 |
| - exit: { |
34 |
| - codeText: exitCodeText, |
35 |
| - table: exitTable, |
36 |
| - tableData: exit, |
37 |
| - tableHeader: exit, |
38 |
| - tableRow: exit |
39 |
| - } |
40 |
| -} |
41 |
| - |
42 |
| -/** @type {FromMarkdownHandle} */ |
43 |
| -function enterTable(token) { |
44 |
| - /** @type {Array<'left'|'right'|'center'|'none'>} */ |
45 |
| - // @ts-expect-error: `align` is custom. |
46 |
| - const align = token._align |
47 |
| - this.enter( |
48 |
| - { |
49 |
| - type: 'table', |
50 |
| - align: align.map((d) => (d === 'none' ? null : d)), |
51 |
| - children: [] |
52 |
| - }, |
53 |
| - token |
54 |
| - ) |
55 |
| - this.setData('inTable', true) |
56 |
| -} |
57 |
| - |
58 |
| -/** @type {FromMarkdownHandle} */ |
59 |
| -function exitTable(token) { |
60 |
| - this.exit(token) |
61 |
| - this.setData('inTable') |
62 |
| -} |
63 |
| - |
64 |
| -/** @type {FromMarkdownHandle} */ |
65 |
| -function enterRow(token) { |
66 |
| - this.enter({type: 'tableRow', children: []}, token) |
67 |
| -} |
68 |
| - |
69 |
| -/** @type {FromMarkdownHandle} */ |
70 |
| -function exit(token) { |
71 |
| - this.exit(token) |
72 |
| -} |
73 |
| - |
74 |
| -/** @type {FromMarkdownHandle} */ |
75 |
| -function enterCell(token) { |
76 |
| - this.enter({type: 'tableCell', children: []}, token) |
77 |
| -} |
78 |
| - |
79 |
| -// Overwrite the default code text data handler to unescape escaped pipes when |
80 |
| -// they are in tables. |
81 |
| -/** @type {FromMarkdownHandle} */ |
82 |
| -function exitCodeText(token) { |
83 |
| - let value = this.resume() |
84 |
| - |
85 |
| - if (this.getData('inTable')) { |
86 |
| - value = value.replace(/\\([\\|])/g, replace) |
87 |
| - } |
88 |
| - |
89 |
| - const node = /** @type {InlineCode} */ (this.stack[this.stack.length - 1]) |
90 |
| - node.value = value |
91 |
| - this.exit(token) |
92 |
| -} |
93 |
| - |
94 |
| -/** |
95 |
| - * @param {string} $0 |
96 |
| - * @param {string} $1 |
97 |
| - * @returns {string} |
98 |
| - */ |
99 |
| -function replace($0, $1) { |
100 |
| - // Pipes work, backslashes don’t (but can’t escape pipes). |
101 |
| - return $1 === '|' ? $1 : $0 |
102 |
| -} |
103 |
| - |
104 |
| -/** |
105 |
| - * @param {Options} [options] |
106 |
| - * @returns {ToMarkdownExtension} |
107 |
| - */ |
108 |
| -export function gfmTableToMarkdown(options) { |
109 |
| - const settings = options || {} |
110 |
| - const padding = settings.tableCellPadding |
111 |
| - const alignDelimiters = settings.tablePipeAlign |
112 |
| - const stringLength = settings.stringLength |
113 |
| - const around = padding ? ' ' : '|' |
114 |
| - |
115 |
| - return { |
116 |
| - unsafe: [ |
117 |
| - {character: '\r', inConstruct: 'tableCell'}, |
118 |
| - {character: '\n', inConstruct: 'tableCell'}, |
119 |
| - // A pipe, when followed by a tab or space (padding), or a dash or colon |
120 |
| - // (unpadded delimiter row), could result in a table. |
121 |
| - {atBreak: true, character: '|', after: '[\t :-]'}, |
122 |
| - // A pipe in a cell must be encoded. |
123 |
| - {character: '|', inConstruct: 'tableCell'}, |
124 |
| - // A colon must be followed by a dash, in which case it could start a |
125 |
| - // delimiter row. |
126 |
| - {atBreak: true, character: ':', after: '-'}, |
127 |
| - // A delimiter row can also start with a dash, when followed by more |
128 |
| - // dashes, a colon, or a pipe. |
129 |
| - // This is a stricter version than the built in check for lists, thematic |
130 |
| - // breaks, and setex heading underlines though: |
131 |
| - // <https://github.com/syntax-tree/mdast-util-to-markdown/blob/51a2038/lib/unsafe.js#L57> |
132 |
| - {atBreak: true, character: '-', after: '[:|-]'} |
133 |
| - ], |
134 |
| - handlers: { |
135 |
| - table: handleTable, |
136 |
| - tableRow: handleTableRow, |
137 |
| - tableCell: handleTableCell, |
138 |
| - inlineCode: inlineCodeWithTable |
139 |
| - } |
140 |
| - } |
141 |
| - |
142 |
| - /** |
143 |
| - * @type {ToMarkdownHandle} |
144 |
| - * @param {Table} node |
145 |
| - */ |
146 |
| - function handleTable(node, _, context, safeOptions) { |
147 |
| - return serializeData( |
148 |
| - handleTableAsData(node, context, safeOptions), |
149 |
| - // @ts-expect-error: fixed in `markdown-table@3.0.1`. |
150 |
| - node.align |
151 |
| - ) |
152 |
| - } |
153 |
| - |
154 |
| - /** |
155 |
| - * This function isn’t really used normally, because we handle rows at the |
156 |
| - * table level. |
157 |
| - * But, if someone passes in a table row, this ensures we make somewhat sense. |
158 |
| - * |
159 |
| - * @type {ToMarkdownHandle} |
160 |
| - * @param {TableRow} node |
161 |
| - */ |
162 |
| - function handleTableRow(node, _, context, safeOptions) { |
163 |
| - const row = handleTableRowAsData(node, context, safeOptions) |
164 |
| - // `markdown-table` will always add an align row |
165 |
| - const value = serializeData([row]) |
166 |
| - return value.slice(0, value.indexOf('\n')) |
167 |
| - } |
168 |
| - |
169 |
| - /** |
170 |
| - * @type {ToMarkdownHandle} |
171 |
| - * @param {TableCell} node |
172 |
| - */ |
173 |
| - function handleTableCell(node, _, context, safeOptions) { |
174 |
| - const exit = context.enter('tableCell') |
175 |
| - const subexit = context.enter('phrasing') |
176 |
| - const value = containerPhrasing(node, context, { |
177 |
| - ...safeOptions, |
178 |
| - before: around, |
179 |
| - after: around |
180 |
| - }) |
181 |
| - subexit() |
182 |
| - exit() |
183 |
| - return value |
184 |
| - } |
185 |
| - |
186 |
| - /** |
187 |
| - * @param {Array<Array<string>>} matrix |
188 |
| - * @param {Array<string>} [align] |
189 |
| - */ |
190 |
| - function serializeData(matrix, align) { |
191 |
| - return markdownTable(matrix, { |
192 |
| - align, |
193 |
| - alignDelimiters, |
194 |
| - padding, |
195 |
| - stringLength |
196 |
| - }) |
197 |
| - } |
198 |
| - |
199 |
| - /** |
200 |
| - * @param {Table} node |
201 |
| - * @param {ToMarkdownContext} context |
202 |
| - * @param {SafeOptions} safeOptions |
203 |
| - */ |
204 |
| - function handleTableAsData(node, context, safeOptions) { |
205 |
| - const children = node.children |
206 |
| - let index = -1 |
207 |
| - /** @type {Array<Array<string>>} */ |
208 |
| - const result = [] |
209 |
| - const subexit = context.enter('table') |
210 |
| - |
211 |
| - while (++index < children.length) { |
212 |
| - result[index] = handleTableRowAsData( |
213 |
| - children[index], |
214 |
| - context, |
215 |
| - safeOptions |
216 |
| - ) |
217 |
| - } |
218 |
| - |
219 |
| - subexit() |
220 |
| - |
221 |
| - return result |
222 |
| - } |
223 |
| - |
224 |
| - /** |
225 |
| - * @param {TableRow} node |
226 |
| - * @param {ToMarkdownContext} context |
227 |
| - * @param {SafeOptions} safeOptions |
228 |
| - */ |
229 |
| - function handleTableRowAsData(node, context, safeOptions) { |
230 |
| - const children = node.children |
231 |
| - let index = -1 |
232 |
| - /** @type {Array<string>} */ |
233 |
| - const result = [] |
234 |
| - const subexit = context.enter('tableRow') |
235 |
| - |
236 |
| - while (++index < children.length) { |
237 |
| - // Note: the positional info as used here is incorrect. |
238 |
| - // Making it correct would be impossible due to aligning cells? |
239 |
| - // And it would need copy/pasting `markdown-table` into this project. |
240 |
| - result[index] = handleTableCell( |
241 |
| - children[index], |
242 |
| - node, |
243 |
| - context, |
244 |
| - safeOptions |
245 |
| - ) |
246 |
| - } |
247 |
| - |
248 |
| - subexit() |
249 |
| - |
250 |
| - return result |
251 |
| - } |
252 |
| - |
253 |
| - /** |
254 |
| - * @type {ToMarkdownHandle} |
255 |
| - * @param {InlineCode} node |
256 |
| - */ |
257 |
| - function inlineCodeWithTable(node, parent, context) { |
258 |
| - let value = inlineCode(node, parent, context) |
259 |
| - |
260 |
| - if (context.stack.includes('tableCell')) { |
261 |
| - value = value.replace(/\|/g, '\\$&') |
262 |
| - } |
263 |
| - |
264 |
| - return value |
265 |
| - } |
266 |
| -} |
| 5 | +export {gfmTableFromMarkdown, gfmTableToMarkdown} from './lib/index.js' |
0 commit comments