confluence-md-sync 0.5.0 → 0.5.1
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 +50 -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,61 @@ 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
|
+
if (headerCount === 0)
|
|
634
|
+
headerCount = 1; // нет <th>-шапки → первая строка как ключи
|
|
635
|
+
if (headerCount >= grid.length)
|
|
636
|
+
headerCount = 1; // не съедать всю таблицу
|
|
637
|
+
const header = grid[headerCount - 1];
|
|
638
|
+
const titleCells = grid
|
|
639
|
+
.slice(0, headerCount - 1)
|
|
640
|
+
.flat()
|
|
641
|
+
.map((c) => c.trim())
|
|
642
|
+
.filter((c) => c !== '');
|
|
643
|
+
const title = [caption, ...titleCells].filter((c) => c !== '').join(' — ');
|
|
620
644
|
const records = [];
|
|
621
|
-
for (const row of grid.slice(
|
|
622
|
-
const
|
|
645
|
+
for (const row of grid.slice(headerCount)) {
|
|
646
|
+
const fields = [];
|
|
623
647
|
row.forEach((val, i) => {
|
|
624
648
|
const key = (header[i] ?? '').trim();
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
649
|
+
if (key === '')
|
|
650
|
+
return;
|
|
651
|
+
// Значение записи — свободный Markdown (не GFM-ячейка): <br> →
|
|
652
|
+
// настоящий перенос, «• » → элементы списка; многострочное значение
|
|
653
|
+
// выносим под заголовок.
|
|
654
|
+
const value = val
|
|
655
|
+
.replace(/<br>/g, '\n')
|
|
656
|
+
.replace(/(^|\n)• /g, '$1- ')
|
|
657
|
+
.trim();
|
|
658
|
+
if (value === '')
|
|
659
|
+
return;
|
|
660
|
+
fields.push(value.includes('\n') ? `**${key}:**\n${value}` : `**${key}:** ${value}`);
|
|
629
661
|
});
|
|
630
|
-
|
|
631
|
-
|
|
662
|
+
// Одиночные значения — поля подряд (одна строка на поле); если есть
|
|
663
|
+
// многострочные (списки), разделяем пустой строкой, чтобы md-список не
|
|
664
|
+
// «склеивался» со следующим заголовком.
|
|
665
|
+
if (fields.length > 0) {
|
|
666
|
+
const sep = fields.some((f) => f.includes('\n')) ? '\n\n' : '\n';
|
|
667
|
+
records.push(fields.join(sep));
|
|
668
|
+
}
|
|
632
669
|
}
|
|
633
670
|
if (records.length === 0)
|
|
634
671
|
throw new Unrepresentable();
|
|
635
672
|
const body = records.join('\n\n---\n\n');
|
|
636
|
-
return
|
|
673
|
+
return title !== '' ? `**${title}**\n\n${body}` : body;
|
|
637
674
|
}
|
|
638
675
|
/**
|
|
639
676
|
* Сплющивает содержимое ячейки в одну строку: абзацы и пункты списков
|
package/package.json
CHANGED