confluence-md-sync 0.5.0 → 0.5.2
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/dist/export/to-markdown.js +51 -13
- package/package.json +1 -1
|
@@ -557,9 +557,12 @@ class Converter {
|
|
|
557
557
|
throw new Unrepresentable();
|
|
558
558
|
const grid = [];
|
|
559
559
|
const aligns = [];
|
|
560
|
+
const headerFlags = [];
|
|
560
561
|
trs.forEach((tr, rowIdx) => {
|
|
561
562
|
if (!grid[rowIdx])
|
|
562
563
|
grid[rowIdx] = [];
|
|
564
|
+
const rowCells = elements(tr.children).filter((c) => c.name === 'td' || c.name === 'th');
|
|
565
|
+
headerFlags[rowIdx] = rowCells.length > 0 && rowCells.every((c) => c.name === 'th');
|
|
563
566
|
let col = 0;
|
|
564
567
|
for (const cell of elements(tr.children)) {
|
|
565
568
|
if (cell.name !== 'td' && cell.name !== 'th')
|
|
@@ -596,7 +599,9 @@ class Converter {
|
|
|
596
599
|
}
|
|
597
600
|
while (aligns.length < width)
|
|
598
601
|
aligns.push('none');
|
|
599
|
-
|
|
602
|
+
while (headerFlags.length < grid.length)
|
|
603
|
+
headerFlags.push(false);
|
|
604
|
+
return { grid, aligns, caption, headerFlags };
|
|
600
605
|
}
|
|
601
606
|
/** Разворачивает любую таблицу в GFM (сетка с заполнением объединений). */
|
|
602
607
|
readableTable(table) {
|
|
@@ -611,29 +616,62 @@ class Converter {
|
|
|
611
616
|
/**
|
|
612
617
|
* tables:'records' — каждую строку тела таблицы разворачивает в запись
|
|
613
618
|
* «**Заголовок:** значение» (пустые ячейки и колонки-филлеры пропускаются),
|
|
614
|
-
* записи разделяются `---`.
|
|
619
|
+
* записи разделяются `---`.
|
|
620
|
+
*
|
|
621
|
+
* Заголовок — ПОСЛЕДНЯЯ из ведущих строк, целиком состоящих из <th>
|
|
622
|
+
* (строки-титулы над ней, например «Детали» с colspan, уходят в подпись).
|
|
623
|
+
* Многострочное значение ячейки разворачивается под заголовком: <br> →
|
|
624
|
+
* перенос строки, буллеты «• » → элементы md-списка.
|
|
615
625
|
*/
|
|
616
626
|
recordsTable(table) {
|
|
617
627
|
this.stats.lossy++;
|
|
618
|
-
const { grid, caption } = this.buildTableGrid(table);
|
|
619
|
-
|
|
628
|
+
const { grid, caption, headerFlags } = this.buildTableGrid(table);
|
|
629
|
+
// Ведущие строки-заголовки: последняя из них — ключи, предыдущие — титулы.
|
|
630
|
+
let headerCount = 0;
|
|
631
|
+
while (headerCount < grid.length && headerFlags[headerCount])
|
|
632
|
+
headerCount++;
|
|
633
|
+
// Нет <th>-шапки (или вся таблица — заголовки): в записи разворачивать
|
|
634
|
+
// нечего — первая строка данных стала бы «ключами» (в т.ч. картинка →
|
|
635
|
+
// ключ `**<img …>:**`). Такую таблицу отдаём как GFM.
|
|
636
|
+
if (headerCount === 0 || headerCount >= grid.length)
|
|
637
|
+
return this.readableTable(table);
|
|
638
|
+
const header = grid[headerCount - 1];
|
|
639
|
+
const titleCells = grid
|
|
640
|
+
.slice(0, headerCount - 1)
|
|
641
|
+
.flat()
|
|
642
|
+
.map((c) => c.trim())
|
|
643
|
+
.filter((c) => c !== '');
|
|
644
|
+
const title = [caption, ...titleCells].filter((c) => c !== '').join(' — ');
|
|
620
645
|
const records = [];
|
|
621
|
-
for (const row of grid.slice(
|
|
622
|
-
const
|
|
646
|
+
for (const row of grid.slice(headerCount)) {
|
|
647
|
+
const fields = [];
|
|
623
648
|
row.forEach((val, i) => {
|
|
624
649
|
const key = (header[i] ?? '').trim();
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
650
|
+
if (key === '')
|
|
651
|
+
return;
|
|
652
|
+
// Значение записи — свободный Markdown (не GFM-ячейка): <br> →
|
|
653
|
+
// настоящий перенос, «• » → элементы списка; многострочное значение
|
|
654
|
+
// выносим под заголовок.
|
|
655
|
+
const value = val
|
|
656
|
+
.replace(/<br>/g, '\n')
|
|
657
|
+
.replace(/(^|\n)• /g, '$1- ')
|
|
658
|
+
.trim();
|
|
659
|
+
if (value === '')
|
|
660
|
+
return;
|
|
661
|
+
fields.push(value.includes('\n') ? `**${key}:**\n${value}` : `**${key}:** ${value}`);
|
|
629
662
|
});
|
|
630
|
-
|
|
631
|
-
|
|
663
|
+
// Одиночные значения — поля подряд (одна строка на поле); если есть
|
|
664
|
+
// многострочные (списки), разделяем пустой строкой, чтобы md-список не
|
|
665
|
+
// «склеивался» со следующим заголовком.
|
|
666
|
+
if (fields.length > 0) {
|
|
667
|
+
const sep = fields.some((f) => f.includes('\n')) ? '\n\n' : '\n';
|
|
668
|
+
records.push(fields.join(sep));
|
|
669
|
+
}
|
|
632
670
|
}
|
|
633
671
|
if (records.length === 0)
|
|
634
672
|
throw new Unrepresentable();
|
|
635
673
|
const body = records.join('\n\n---\n\n');
|
|
636
|
-
return
|
|
674
|
+
return title !== '' ? `**${title}**\n\n${body}` : body;
|
|
637
675
|
}
|
|
638
676
|
/**
|
|
639
677
|
* Сплющивает содержимое ячейки в одну строку: абзацы и пункты списков
|
package/package.json
CHANGED