confluence-md-sync 0.8.8 → 0.8.10
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 +32 -4
- package/dist/macros/registry.js +4 -2
- package/dist/macros/xml.d.ts +14 -1
- package/dist/macros/xml.js +20 -1
- package/package.json +1 -1
|
@@ -147,10 +147,38 @@ class Converter {
|
|
|
147
147
|
// tables:'records' — любую таблицу разворачиваем в записи.
|
|
148
148
|
if (this.tablesAsRecords)
|
|
149
149
|
return this.recordsTable(el);
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
150
|
+
try {
|
|
151
|
+
return this.tableToMd(el);
|
|
152
|
+
}
|
|
153
|
+
catch (e) {
|
|
154
|
+
if (!(e instanceof Unrepresentable))
|
|
155
|
+
throw e;
|
|
156
|
+
// Строгая GFM-конвертация отклонила таблицу: то ли из-за косметики
|
|
157
|
+
// (Confluence лепит class="wrapped" style="width:…%" на КАЖДУЮ
|
|
158
|
+
// таблицу, а первую строку часто оформляет жирным <td> вместо <th>),
|
|
159
|
+
// то ли из-за реальных объединений (colspan/rowspan). В faithful без
|
|
160
|
+
// этой ветки ЛЮБАЯ такая таблица падала в дословный <table> HTML —
|
|
161
|
+
// нормализуем в GFM тем же путём, что readable (buildTableGrid не
|
|
162
|
+
// смотрит на атрибуты/семантику заголовка): оформление и объединения
|
|
163
|
+
// теряются при повторной публикации, но данные — md-таблица, не теги.
|
|
164
|
+
if (!this.readable) {
|
|
165
|
+
const saved = this.readable;
|
|
166
|
+
this.readable = true;
|
|
167
|
+
try {
|
|
168
|
+
const norm = this.readableTable(el);
|
|
169
|
+
this.stats.normalized++;
|
|
170
|
+
return norm;
|
|
171
|
+
}
|
|
172
|
+
catch (e2) {
|
|
173
|
+
if (!(e2 instanceof Unrepresentable))
|
|
174
|
+
throw e2;
|
|
175
|
+
}
|
|
176
|
+
finally {
|
|
177
|
+
this.readable = saved;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
throw e;
|
|
181
|
+
}
|
|
154
182
|
}
|
|
155
183
|
if (el.name === 'ac:structured-macro')
|
|
156
184
|
return this.macroToMd(el);
|
package/dist/macros/registry.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Markdown } from '../markdown/markdown.js';
|
|
2
2
|
import { unescapeParamValue } from './builder.js';
|
|
3
|
-
import {
|
|
3
|
+
import { stableMacroId, structuredMacro } from './xml.js';
|
|
4
4
|
/**
|
|
5
5
|
* Реестр макросов. Пустой при создании — наполняется плагинами через
|
|
6
6
|
* {@link use} или точечной регистрацией через {@link register}.
|
|
@@ -126,7 +126,9 @@ function processMacroType(storage, macroName, renderer) {
|
|
|
126
126
|
}
|
|
127
127
|
const bodyStart = startIdx + match[0].length;
|
|
128
128
|
const body = storage.substring(bodyStart, endIdx).trim();
|
|
129
|
-
|
|
129
|
+
// Стабильный id от содержимого маркера — иначе он менялся бы на КАЖДЫЙ
|
|
130
|
+
// рендер (см. stableMacroId), ломая content-hash идемпотентность publish.
|
|
131
|
+
const macroId = stableMacroId(`${macroName}\u0000${paramStr}\u0000${body}`);
|
|
130
132
|
const macroXhtml = renderer({ params, body, macroId });
|
|
131
133
|
const before = storage.substring(0, startIdx);
|
|
132
134
|
const after = storage.substring(endIdx + endMarker.length);
|
package/dist/macros/xml.d.ts
CHANGED
|
@@ -2,8 +2,21 @@
|
|
|
2
2
|
import type { MacroParam } from './types.js';
|
|
3
3
|
/** Экранирует строку для XML-атрибутов и текстовых узлов. */
|
|
4
4
|
export declare function escapeXmlAttr(s: string): string;
|
|
5
|
-
/** Генерирует UUID для ac:macro-id. */
|
|
5
|
+
/** Генерирует случайный UUID для ac:macro-id (когда стабильность не нужна). */
|
|
6
6
|
export declare function generateMacroId(): string;
|
|
7
|
+
/**
|
|
8
|
+
* Детерминированный macro-id (UUID-формы) от seed — как правило, от исходного
|
|
9
|
+
* содержимого маркера (имя+параметры+тело). ВАЖНО для идемпотентности
|
|
10
|
+
* content-hash: если id генерировать случайно на каждый рендер (см.
|
|
11
|
+
* {@link generateMacroId}), storage у страницы с любым макросом отличается
|
|
12
|
+
* при каждой публикации, даже когда исходный markdown не менялся — hash
|
|
13
|
+
* никогда не совпадает, и publish принудительно обновляет страницу заново
|
|
14
|
+
* при каждом прогоне (замечено на проде: 1 изменённый файл → 250+ страниц
|
|
15
|
+
* «обновились», потому что ~250 из них содержат макрос details/properties).
|
|
16
|
+
* Стабильный id по содержимому убирает этот псевдо-дрейф: одинаковый маркер
|
|
17
|
+
* даёт одинаковый storage → hash совпадает → UNCHANGED.
|
|
18
|
+
*/
|
|
19
|
+
export declare function stableMacroId(seed: string): string;
|
|
7
20
|
export interface StructuredMacroOptions {
|
|
8
21
|
/**
|
|
9
22
|
* Macro parameters. `undefined` values are skipped. A key rendered as
|
package/dist/macros/xml.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/** XML helpers shared by macro renderers. */
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
2
3
|
/** Экранирует строку для XML-атрибутов и текстовых узлов. */
|
|
3
4
|
export function escapeXmlAttr(s) {
|
|
4
5
|
return s.replace(/[<>&"']/g, (c) => {
|
|
@@ -12,7 +13,7 @@ export function escapeXmlAttr(s) {
|
|
|
12
13
|
}
|
|
13
14
|
});
|
|
14
15
|
}
|
|
15
|
-
/** Генерирует UUID для ac:macro-id. */
|
|
16
|
+
/** Генерирует случайный UUID для ac:macro-id (когда стабильность не нужна). */
|
|
16
17
|
export function generateMacroId() {
|
|
17
18
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
18
19
|
const r = (Math.random() * 16) | 0;
|
|
@@ -20,6 +21,24 @@ export function generateMacroId() {
|
|
|
20
21
|
return v.toString(16);
|
|
21
22
|
});
|
|
22
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Детерминированный macro-id (UUID-формы) от seed — как правило, от исходного
|
|
26
|
+
* содержимого маркера (имя+параметры+тело). ВАЖНО для идемпотентности
|
|
27
|
+
* content-hash: если id генерировать случайно на каждый рендер (см.
|
|
28
|
+
* {@link generateMacroId}), storage у страницы с любым макросом отличается
|
|
29
|
+
* при каждой публикации, даже когда исходный markdown не менялся — hash
|
|
30
|
+
* никогда не совпадает, и publish принудительно обновляет страницу заново
|
|
31
|
+
* при каждом прогоне (замечено на проде: 1 изменённый файл → 250+ страниц
|
|
32
|
+
* «обновились», потому что ~250 из них содержат макрос details/properties).
|
|
33
|
+
* Стабильный id по содержимому убирает этот псевдо-дрейф: одинаковый маркер
|
|
34
|
+
* даёт одинаковый storage → hash совпадает → UNCHANGED.
|
|
35
|
+
*/
|
|
36
|
+
export function stableMacroId(seed) {
|
|
37
|
+
const hex = createHash('sha256').update(seed, 'utf-8').digest('hex');
|
|
38
|
+
const bytes = hex.slice(0, 32);
|
|
39
|
+
return (`${bytes.slice(0, 8)}-${bytes.slice(8, 12)}-4${bytes.slice(13, 16)}-` +
|
|
40
|
+
`${'89ab'[parseInt(bytes[16], 16) % 4]}${bytes.slice(17, 20)}-${bytes.slice(20, 32)}`);
|
|
41
|
+
}
|
|
23
42
|
/**
|
|
24
43
|
* Assembles an `<ac:structured-macro>` element. Takes care of parameter
|
|
25
44
|
* escaping, rich vs plain bodies and CDATA safety.
|
package/package.json
CHANGED