markdown-to-slack-blocks 1.4.0 → 1.5.0
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 +28 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +26 -7
- package/dist/splitter.d.ts +5 -1
- package/dist/splitter.d.ts.map +1 -1
- package/dist/splitter.js +396 -0
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/validator.d.ts +2 -1
- package/dist/validator.d.ts.map +1 -1
- package/dist/validator.js +36 -19
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -181,3 +181,31 @@ const text = blocksToPlainText(blocks);
|
|
|
181
181
|
```
|
|
182
182
|
|
|
183
183
|
The function walks the rendered blocks and returns a joined string that keeps list markers, quotes, tables (as `cell | cell` rows), mentions, dates (using the provided fallback or ISO string), and basic formatting markers where possible. The output is best-effort and is intended for concise fallbacks rather than full fidelity rendering.
|
|
184
|
+
|
|
185
|
+
### Markdown rendering
|
|
186
|
+
|
|
187
|
+
If you need a best-effort Markdown representation of an existing block array, use `blocksToMarkdown`:
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
import { blocksToMarkdown } from 'markdown-to-slack-blocks';
|
|
191
|
+
|
|
192
|
+
const markdown = blocksToMarkdown(blocks);
|
|
193
|
+
// -> "# Title\n\nParagraph with **bold** text" or similar
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
If you want Slack IDs rendered back to visible mention names (which might be useful for LLMs), pass the inverse mention map:
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
const markdown = blocksToMarkdown(blocks, {
|
|
200
|
+
mentions: {
|
|
201
|
+
users: { 'U123456': 'username' },
|
|
202
|
+
channels: { 'C123456': 'general' },
|
|
203
|
+
userGroups: { 'S123456': 'engineers' },
|
|
204
|
+
teams: { 'T123456': 'myteam' }
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
With these options, mentions like `<@U123456>` or `<#C123456>` are rendered as `@username` and `#general` where possible. The same ID format validation applies to these reverse mention maps.
|
|
210
|
+
|
|
211
|
+
The renderer converts `rich_text`, section `mrkdwn`, code blocks, quotes, lists, tables, images, and Slack entities back into normalized Markdown. Because Slack blocks do not preserve every detail of the original source, the output is canonicalized rather than byte-for-byte identical to the input, but it round-trips cleanly for this library's generated blocks.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { MarkdownToBlocksOptions } from "./types";
|
|
2
2
|
export * from "./types";
|
|
3
|
-
export { blocksToPlainText, splitBlocks, splitBlocksWithText, SplitBlocksOptions, SplitBlocksResult, } from "./splitter";
|
|
3
|
+
export { blocksToMarkdown, blocksToPlainText, splitBlocks, splitBlocksWithText, SplitBlocksOptions, SplitBlocksResult, } from "./splitter";
|
|
4
4
|
export declare function markdownToBlocks(markdown: string, options?: MarkdownToBlocksOptions): import("./types").Block[];
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAIvD,cAAc,SAAS,CAAC;AACxB,OAAO,EACN,iBAAiB,EACjB,WAAW,EACX,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,GACjB,MAAM,YAAY,CAAC;AAEpB,wBAAgB,gBAAgB,CAC/B,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,uBAAuB,6BAIjC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAIvD,cAAc,SAAS,CAAC;AACxB,OAAO,EACN,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,GACjB,MAAM,YAAY,CAAC;AAEpB,wBAAgB,gBAAgB,CAC/B,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,uBAAuB,6BAIjC"}
|
package/dist/index.js
CHANGED
|
@@ -14,13 +14,14 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.splitBlocksWithText = exports.splitBlocks = exports.blocksToPlainText = void 0;
|
|
17
|
+
exports.splitBlocksWithText = exports.splitBlocks = exports.blocksToPlainText = exports.blocksToMarkdown = void 0;
|
|
18
18
|
exports.markdownToBlocks = markdownToBlocks;
|
|
19
19
|
const parser_1 = require("./parser");
|
|
20
20
|
const validator_1 = require("./validator");
|
|
21
21
|
// Re-export types for consumers
|
|
22
22
|
__exportStar(require("./types"), exports);
|
|
23
23
|
var splitter_1 = require("./splitter");
|
|
24
|
+
Object.defineProperty(exports, "blocksToMarkdown", { enumerable: true, get: function () { return splitter_1.blocksToMarkdown; } });
|
|
24
25
|
Object.defineProperty(exports, "blocksToPlainText", { enumerable: true, get: function () { return splitter_1.blocksToPlainText; } });
|
|
25
26
|
Object.defineProperty(exports, "splitBlocks", { enumerable: true, get: function () { return splitter_1.splitBlocks; } });
|
|
26
27
|
Object.defineProperty(exports, "splitBlocksWithText", { enumerable: true, get: function () { return splitter_1.splitBlocksWithText; } });
|
package/dist/parser.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACX,KAAK,EAKL,uBAAuB,EAGvB,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACX,KAAK,EAKL,uBAAuB,EAGvB,MAAM,SAAS,CAAC;AAyGjB,wBAAgB,aAAa,CAC5B,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,uBAA4B,GACnC,KAAK,EAAE,CAiNT"}
|
package/dist/parser.js
CHANGED
|
@@ -24,12 +24,25 @@ function isListNode(node) {
|
|
|
24
24
|
/**
|
|
25
25
|
* Preprocess markdown to "unwrap" lists that are entirely wrapped in formatting.
|
|
26
26
|
* For example: "**1. Item**" becomes "1. **Item**"
|
|
27
|
+
* Also handles heading-prefixed format-wrapped lists:
|
|
28
|
+
* "### **1. Item**" becomes "###\n1. **Item**"
|
|
27
29
|
* This allows the markdown parser to recognize them as list items.
|
|
28
30
|
*/
|
|
29
31
|
function preprocessMarkdown(markdown) {
|
|
30
32
|
const lines = markdown.split("\n");
|
|
31
|
-
const processedLines =
|
|
33
|
+
const processedLines = [];
|
|
34
|
+
for (const line of lines) {
|
|
32
35
|
const trimmed = line.trim();
|
|
36
|
+
// Match heading-prefixed format-wrapped lists
|
|
37
|
+
// e.g. "### **1. Item**" or "## *- Item*"
|
|
38
|
+
const headingFormatWrappedListRegex = /^(#{1,6})\s+(\*\*|\*|_|~)(\d+\.|\*|-|\+)(\s+)(.*?)\2$/;
|
|
39
|
+
const headingMatch = trimmed.match(headingFormatWrappedListRegex);
|
|
40
|
+
if (headingMatch) {
|
|
41
|
+
const [_, _heading, format, marker, spaces, content] = headingMatch;
|
|
42
|
+
// Split into an empty line (to end the heading context) and a format-unwrapped list line
|
|
43
|
+
processedLines.push(`${marker}${spaces}${format}${content}${format}`);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
33
46
|
// Match patterns like **1. item**, *1. item*, ~1. item~, **- item**, etc.
|
|
34
47
|
// Group 1: opening format (** or * or _ or ~)
|
|
35
48
|
// Group 2: list marker (1. or - or * or +)
|
|
@@ -42,10 +55,11 @@ function preprocessMarkdown(markdown) {
|
|
|
42
55
|
const [_, format, marker, spaces, content] = match;
|
|
43
56
|
// Preserve leading whitespace of the original line
|
|
44
57
|
const leadingWhitespace = line.match(/^\s*/)?.[0] || "";
|
|
45
|
-
|
|
58
|
+
processedLines.push(`${leadingWhitespace}${marker}${spaces}${format}${content}${format}`);
|
|
59
|
+
continue;
|
|
46
60
|
}
|
|
47
|
-
|
|
48
|
-
}
|
|
61
|
+
processedLines.push(line);
|
|
62
|
+
}
|
|
49
63
|
return processedLines.join("\n");
|
|
50
64
|
}
|
|
51
65
|
function parseMarkdown(markdown, options = {}) {
|
|
@@ -382,9 +396,14 @@ function processTextNode(text, style, options) {
|
|
|
382
396
|
elements.push(withStyle({ type: "channel", channel_id: channelId }));
|
|
383
397
|
}
|
|
384
398
|
else if (match[8]) {
|
|
385
|
-
// Team: <!subteam^ID>
|
|
386
|
-
const
|
|
387
|
-
|
|
399
|
+
// User Group / Team: <!subteam^ID>
|
|
400
|
+
const subteamId = match[8];
|
|
401
|
+
if (subteamId.startsWith("S")) {
|
|
402
|
+
elements.push(withStyle({ type: "usergroup", usergroup_id: subteamId }));
|
|
403
|
+
}
|
|
404
|
+
else {
|
|
405
|
+
elements.push(withStyle({ type: "team", team_id: subteamId }));
|
|
406
|
+
}
|
|
388
407
|
}
|
|
389
408
|
else if (match[10]) {
|
|
390
409
|
// Date: <!date^...|...>
|
package/dist/splitter.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Block } from "./types";
|
|
1
|
+
import type { Block, BlocksToMarkdownOptions } from "./types";
|
|
2
2
|
export interface SplitBlocksOptions {
|
|
3
3
|
/** Maximum number of blocks per message. Default: 40 */
|
|
4
4
|
maxBlocks?: number;
|
|
@@ -22,6 +22,10 @@ export declare function splitBlocks(blocks: Block[], options?: SplitBlocksOption
|
|
|
22
22
|
* Splits blocks and also returns a plain-text fallback for each batch, suitable for postMessage `text`.
|
|
23
23
|
*/
|
|
24
24
|
export declare function splitBlocksWithText(blocks: Block[], options?: SplitBlocksOptions): SplitBlocksResult[];
|
|
25
|
+
/**
|
|
26
|
+
* Renders a block array back into best-effort Markdown.
|
|
27
|
+
*/
|
|
28
|
+
export declare function blocksToMarkdown(blocks: Block[], options?: BlocksToMarkdownOptions): string;
|
|
25
29
|
/**
|
|
26
30
|
* Generates a lightweight plain-text fallback from a block batch.
|
|
27
31
|
*/
|
package/dist/splitter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"splitter.d.ts","sourceRoot":"","sources":["../src/splitter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,KAAK,
|
|
1
|
+
{"version":3,"file":"splitter.d.ts","sourceRoot":"","sources":["../src/splitter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,KAAK,EACL,uBAAuB,EAcvB,MAAM,SAAS,CAAC;AAGjB,MAAM,WAAW,kBAAkB;IAClC,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,KAAK,EAAE,CAAC;CAChB;AAMD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAC1B,MAAM,EAAE,KAAK,EAAE,EACf,OAAO,CAAC,EAAE,kBAAkB,GAC1B,KAAK,EAAE,EAAE,CAqFX;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAClC,MAAM,EAAE,KAAK,EAAE,EACf,OAAO,CAAC,EAAE,kBAAkB,GAC1B,iBAAiB,EAAE,CAMrB;AAmSD;;GAEG;AACH,wBAAgB,gBAAgB,CAC/B,MAAM,EAAE,KAAK,EAAE,EACf,OAAO,CAAC,EAAE,uBAAuB,GAC/B,MAAM,CAWR;AAkjBD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CA4GzD"}
|
package/dist/splitter.js
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.splitBlocks = splitBlocks;
|
|
4
4
|
exports.splitBlocksWithText = splitBlocksWithText;
|
|
5
|
+
exports.blocksToMarkdown = blocksToMarkdown;
|
|
5
6
|
exports.blocksToPlainText = blocksToPlainText;
|
|
7
|
+
const validator_1 = require("./validator");
|
|
6
8
|
const DEFAULT_MAX_BLOCKS = 40;
|
|
7
9
|
const DEFAULT_MAX_CHARACTERS = 12000;
|
|
8
10
|
const DEFAULT_MAX_TEXT_SECTION_CHARACTERS = 3000;
|
|
@@ -332,6 +334,400 @@ function chunkString(str, limit) {
|
|
|
332
334
|
}
|
|
333
335
|
return chunks;
|
|
334
336
|
}
|
|
337
|
+
/**
|
|
338
|
+
* Renders a block array back into best-effort Markdown.
|
|
339
|
+
*/
|
|
340
|
+
function blocksToMarkdown(blocks, options) {
|
|
341
|
+
(0, validator_1.validateBlocksToMarkdownOptions)(options);
|
|
342
|
+
const parts = [];
|
|
343
|
+
for (const block of blocks) {
|
|
344
|
+
const rendered = renderBlockAsMarkdown(block, options).trim();
|
|
345
|
+
if (rendered)
|
|
346
|
+
parts.push(rendered);
|
|
347
|
+
}
|
|
348
|
+
return parts.join("\n\n");
|
|
349
|
+
}
|
|
350
|
+
function renderBlockAsMarkdown(block, options) {
|
|
351
|
+
switch (block.type) {
|
|
352
|
+
case "section":
|
|
353
|
+
return renderSectionBlockAsMarkdown(block, options);
|
|
354
|
+
case "header":
|
|
355
|
+
return `# ${block.text.text}`;
|
|
356
|
+
case "context":
|
|
357
|
+
return block.elements
|
|
358
|
+
.map((element) => element.type === "image"
|
|
359
|
+
? renderImageElementAsMarkdown(element)
|
|
360
|
+
: renderTextObjectAsMarkdown(element, options))
|
|
361
|
+
.filter(Boolean)
|
|
362
|
+
.join(" ");
|
|
363
|
+
case "rich_text":
|
|
364
|
+
return renderRichTextBlockAsMarkdown(block, options);
|
|
365
|
+
case "divider":
|
|
366
|
+
return "---";
|
|
367
|
+
case "image":
|
|
368
|
+
return renderImageBlockAsMarkdown(block);
|
|
369
|
+
case "table":
|
|
370
|
+
return renderTableBlockAsMarkdown(block, options);
|
|
371
|
+
default:
|
|
372
|
+
return "";
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
function renderSectionBlockAsMarkdown(block, options) {
|
|
376
|
+
const parts = [];
|
|
377
|
+
const text = renderTextObjectAsMarkdown(block.text, options);
|
|
378
|
+
if (text)
|
|
379
|
+
parts.push(text);
|
|
380
|
+
if (block.fields?.length) {
|
|
381
|
+
const renderedFields = block.fields
|
|
382
|
+
.map((field) => renderTextObjectAsMarkdown(field, options))
|
|
383
|
+
.filter(Boolean)
|
|
384
|
+
.join("\n");
|
|
385
|
+
if (renderedFields)
|
|
386
|
+
parts.push(renderedFields);
|
|
387
|
+
}
|
|
388
|
+
if (isImageElement(block.accessory)) {
|
|
389
|
+
parts.push(renderImageElementAsMarkdown(block.accessory));
|
|
390
|
+
}
|
|
391
|
+
return parts.join("\n\n");
|
|
392
|
+
}
|
|
393
|
+
function renderTextObjectAsMarkdown(text, options) {
|
|
394
|
+
if (!text)
|
|
395
|
+
return "";
|
|
396
|
+
return text.type === "mrkdwn"
|
|
397
|
+
? convertMrkdwnToMarkdown(text.text, options)
|
|
398
|
+
: text.text;
|
|
399
|
+
}
|
|
400
|
+
function renderRichTextBlockAsMarkdown(block, options) {
|
|
401
|
+
const heading = renderRichTextHeadingAsMarkdown(block);
|
|
402
|
+
if (heading)
|
|
403
|
+
return heading;
|
|
404
|
+
const rendered = block.elements
|
|
405
|
+
.map((element) => renderRichTextElementAsMarkdown(element, options))
|
|
406
|
+
.filter((part) => part.markdown.length > 0);
|
|
407
|
+
if (rendered.length === 0)
|
|
408
|
+
return "";
|
|
409
|
+
let markdown = rendered[0].markdown;
|
|
410
|
+
for (let index = 1; index < rendered.length; index++) {
|
|
411
|
+
const previous = rendered[index - 1];
|
|
412
|
+
const current = rendered[index];
|
|
413
|
+
const separator = previous.kind === "list" && current.kind === "list" ? "\n" : "\n\n";
|
|
414
|
+
markdown += separator + current.markdown;
|
|
415
|
+
}
|
|
416
|
+
return markdown;
|
|
417
|
+
}
|
|
418
|
+
function renderRichTextHeadingAsMarkdown(block) {
|
|
419
|
+
if (block.elements.length !== 1)
|
|
420
|
+
return "";
|
|
421
|
+
const [element] = block.elements;
|
|
422
|
+
if (element.type !== "rich_text_section" || element.elements.length !== 1) {
|
|
423
|
+
return "";
|
|
424
|
+
}
|
|
425
|
+
const [textElement] = element.elements;
|
|
426
|
+
if (textElement.type !== "text" ||
|
|
427
|
+
!textElement.style?.bold ||
|
|
428
|
+
textElement.style.italic ||
|
|
429
|
+
textElement.style.strike ||
|
|
430
|
+
textElement.style.code) {
|
|
431
|
+
return "";
|
|
432
|
+
}
|
|
433
|
+
return `### ${textElement.text}`;
|
|
434
|
+
}
|
|
435
|
+
function renderRichTextElementAsMarkdown(element, options) {
|
|
436
|
+
switch (element.type) {
|
|
437
|
+
case "rich_text_section":
|
|
438
|
+
return {
|
|
439
|
+
kind: "section",
|
|
440
|
+
markdown: renderRichTextSectionAsMarkdown(element, options),
|
|
441
|
+
};
|
|
442
|
+
case "rich_text_list":
|
|
443
|
+
return {
|
|
444
|
+
kind: "list",
|
|
445
|
+
markdown: renderRichTextListAsMarkdown(element, options),
|
|
446
|
+
};
|
|
447
|
+
case "rich_text_preformatted":
|
|
448
|
+
return {
|
|
449
|
+
kind: "preformatted",
|
|
450
|
+
markdown: renderRichTextPreformattedAsMarkdown(element, options),
|
|
451
|
+
};
|
|
452
|
+
case "rich_text_quote":
|
|
453
|
+
return {
|
|
454
|
+
kind: "quote",
|
|
455
|
+
markdown: renderRichTextQuoteAsMarkdown(element, options),
|
|
456
|
+
};
|
|
457
|
+
default:
|
|
458
|
+
return {
|
|
459
|
+
kind: "section",
|
|
460
|
+
markdown: "",
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
function renderRichTextSectionAsMarkdown(section, options) {
|
|
465
|
+
return section.elements
|
|
466
|
+
.map((element) => renderRichTextSectionElementAsMarkdown(element, options))
|
|
467
|
+
.join("");
|
|
468
|
+
}
|
|
469
|
+
function renderRichTextListAsMarkdown(list, options) {
|
|
470
|
+
const indentUnit = list.style === "ordered" ? " " : " ";
|
|
471
|
+
const indent = indentUnit.repeat(list.indent ?? 0);
|
|
472
|
+
const start = list.offset ?? 1;
|
|
473
|
+
return list.elements
|
|
474
|
+
.map((item, index) => {
|
|
475
|
+
const marker = list.style === "ordered" ? `${start + index}. ` : "- ";
|
|
476
|
+
const content = renderRichTextSectionAsMarkdown(item, options);
|
|
477
|
+
return indentMultilineMarkdown(`${indent}${marker}`, content);
|
|
478
|
+
})
|
|
479
|
+
.join("\n");
|
|
480
|
+
}
|
|
481
|
+
function renderRichTextPreformattedAsMarkdown(element, options) {
|
|
482
|
+
const text = element.elements
|
|
483
|
+
.map((item) => renderRichTextSectionElementAsMarkdown(item, options))
|
|
484
|
+
.join("");
|
|
485
|
+
return wrapFencedCodeBlock(text);
|
|
486
|
+
}
|
|
487
|
+
function renderRichTextQuoteAsMarkdown(element, options) {
|
|
488
|
+
return renderRichTextSectionAsMarkdown({
|
|
489
|
+
type: "rich_text_section",
|
|
490
|
+
elements: element.elements,
|
|
491
|
+
}, options)
|
|
492
|
+
.split("\n")
|
|
493
|
+
.map((line) => `> ${line}`)
|
|
494
|
+
.join("\n");
|
|
495
|
+
}
|
|
496
|
+
function renderRichTextSectionElementAsMarkdown(element, options) {
|
|
497
|
+
switch (element.type) {
|
|
498
|
+
case "text":
|
|
499
|
+
return applyMarkdownStyle(element.text, element.style);
|
|
500
|
+
case "link": {
|
|
501
|
+
const content = element.text && element.text.length > 0
|
|
502
|
+
? `[${element.text}](<${element.url}>)`
|
|
503
|
+
: element.url;
|
|
504
|
+
return applyMarkdownStyle(content, element.style);
|
|
505
|
+
}
|
|
506
|
+
case "emoji":
|
|
507
|
+
return applyMarkdownStyle(`:${element.name}:`, element.style);
|
|
508
|
+
case "date": {
|
|
509
|
+
const fallback = element.fallback ?? new Date(element.timestamp * 1000).toISOString();
|
|
510
|
+
const content = `<!date^${element.timestamp}^${element.format}|${fallback}>`;
|
|
511
|
+
return applyMarkdownStyle(content, element.style);
|
|
512
|
+
}
|
|
513
|
+
case "user":
|
|
514
|
+
return applyMarkdownStyle(renderUserMentionAsMarkdown(element.user_id, options), element.style);
|
|
515
|
+
case "usergroup":
|
|
516
|
+
return applyMarkdownStyle(renderUserGroupMentionAsMarkdown(element.usergroup_id, options), element.style);
|
|
517
|
+
case "team":
|
|
518
|
+
return applyMarkdownStyle(renderTeamMentionAsMarkdown(element.team_id, options), element.style);
|
|
519
|
+
case "channel":
|
|
520
|
+
return applyMarkdownStyle(renderChannelMentionAsMarkdown(element.channel_id, options), element.style);
|
|
521
|
+
case "broadcast":
|
|
522
|
+
return applyMarkdownStyle(`<!${element.range}>`, element.style);
|
|
523
|
+
case "color":
|
|
524
|
+
return applyMarkdownStyle(element.value, element.style);
|
|
525
|
+
default:
|
|
526
|
+
return "";
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
function renderImageBlockAsMarkdown(block) {
|
|
530
|
+
const title = block.title?.text
|
|
531
|
+
? ` "${block.title.text.replace(/"/g, '\\"')}"`
|
|
532
|
+
: "";
|
|
533
|
+
return ``;
|
|
534
|
+
}
|
|
535
|
+
function renderImageElementAsMarkdown(element) {
|
|
536
|
+
return ``;
|
|
537
|
+
}
|
|
538
|
+
function renderTableBlockAsMarkdown(block, options) {
|
|
539
|
+
if (block.rows.length === 0 || block.rows[0].length === 0) {
|
|
540
|
+
return "";
|
|
541
|
+
}
|
|
542
|
+
const rows = block.rows.map((row) => `| ${row.map((cell) => renderTableCellAsMarkdown(cell, options)).join(" | ")} |`);
|
|
543
|
+
const separator = `| ${block.rows[0].map(() => "---").join(" | ")} |`;
|
|
544
|
+
return [rows[0], separator, ...rows.slice(1)].join("\n");
|
|
545
|
+
}
|
|
546
|
+
function renderTableCellAsMarkdown(cell, options) {
|
|
547
|
+
return renderRichTextBlockAsMarkdown(cell, options)
|
|
548
|
+
.replace(/\|/g, "\\|")
|
|
549
|
+
.replace(/\n/g, "\\n");
|
|
550
|
+
}
|
|
551
|
+
function applyMarkdownStyle(text, style) {
|
|
552
|
+
if (!style)
|
|
553
|
+
return text;
|
|
554
|
+
let result = text;
|
|
555
|
+
if (style.code) {
|
|
556
|
+
result = wrapInlineCode(result);
|
|
557
|
+
}
|
|
558
|
+
if (style.bold) {
|
|
559
|
+
result = `**${result}**`;
|
|
560
|
+
}
|
|
561
|
+
if (style.italic) {
|
|
562
|
+
result = `*${result}*`;
|
|
563
|
+
}
|
|
564
|
+
if (style.strike) {
|
|
565
|
+
result = `~${result}~`;
|
|
566
|
+
}
|
|
567
|
+
return result;
|
|
568
|
+
}
|
|
569
|
+
function wrapInlineCode(text) {
|
|
570
|
+
const fence = getBacktickFence(text, 1);
|
|
571
|
+
return `${fence}${text}${fence}`;
|
|
572
|
+
}
|
|
573
|
+
function wrapFencedCodeBlock(text) {
|
|
574
|
+
const fence = getBacktickFence(text, 3);
|
|
575
|
+
return `${fence}\n${text}\n${fence}`;
|
|
576
|
+
}
|
|
577
|
+
function getBacktickFence(text, minimumLength) {
|
|
578
|
+
const runs = text.match(/`+/g);
|
|
579
|
+
const longestRun = runs?.reduce((max, run) => Math.max(max, run.length), 0) ?? 0;
|
|
580
|
+
return "`".repeat(Math.max(minimumLength, longestRun + 1));
|
|
581
|
+
}
|
|
582
|
+
function indentMultilineMarkdown(prefix, text) {
|
|
583
|
+
const lines = text.split("\n");
|
|
584
|
+
if (lines.length === 0)
|
|
585
|
+
return prefix.trimEnd();
|
|
586
|
+
const continuation = " ".repeat(prefix.length);
|
|
587
|
+
return lines
|
|
588
|
+
.map((line, index) => index === 0 ? `${prefix}${line}` : `${continuation}${line}`)
|
|
589
|
+
.join("\n");
|
|
590
|
+
}
|
|
591
|
+
function convertMrkdwnToMarkdown(text, options) {
|
|
592
|
+
let result = "";
|
|
593
|
+
for (let index = 0; index < text.length; index++) {
|
|
594
|
+
const character = text[index];
|
|
595
|
+
if (character === "`") {
|
|
596
|
+
const closingIndex = text.indexOf("`", index + 1);
|
|
597
|
+
if (closingIndex !== -1) {
|
|
598
|
+
result += wrapInlineCode(text.slice(index + 1, closingIndex));
|
|
599
|
+
index = closingIndex;
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
if (character === "<") {
|
|
604
|
+
const closingIndex = text.indexOf(">", index + 1);
|
|
605
|
+
if (closingIndex !== -1) {
|
|
606
|
+
result += convertAngleBracketTokenToMarkdown(text.slice(index, closingIndex + 1), options);
|
|
607
|
+
index = closingIndex;
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
if (isMrkdwnStyleMarker(character) && isValidMrkdwnStyleOpen(text, index)) {
|
|
612
|
+
const closingIndex = findClosingMrkdwnStyleMarker(text, index);
|
|
613
|
+
if (closingIndex !== -1) {
|
|
614
|
+
const inner = convertMrkdwnToMarkdown(text.slice(index + 1, closingIndex), options);
|
|
615
|
+
result += wrapConvertedMrkdwnStyle(character, inner);
|
|
616
|
+
index = closingIndex;
|
|
617
|
+
continue;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
result += character;
|
|
621
|
+
}
|
|
622
|
+
return result;
|
|
623
|
+
}
|
|
624
|
+
function convertAngleBracketTokenToMarkdown(token, options) {
|
|
625
|
+
const userMatch = token.match(/^<@([\w.-]+)>$/);
|
|
626
|
+
if (userMatch) {
|
|
627
|
+
return renderUserMentionAsMarkdown(userMatch[1], options);
|
|
628
|
+
}
|
|
629
|
+
const channelMatch = token.match(/^<#([\w.-]+)>$/);
|
|
630
|
+
if (channelMatch) {
|
|
631
|
+
return renderChannelMentionAsMarkdown(channelMatch[1], options);
|
|
632
|
+
}
|
|
633
|
+
const subteamMatch = token.match(/^<!subteam\^([\w.-]+)>$/);
|
|
634
|
+
if (subteamMatch) {
|
|
635
|
+
const subteamId = subteamMatch[1];
|
|
636
|
+
return subteamId.startsWith("S")
|
|
637
|
+
? renderUserGroupMentionAsMarkdown(subteamId, options)
|
|
638
|
+
: renderTeamMentionAsMarkdown(subteamId, options);
|
|
639
|
+
}
|
|
640
|
+
if (token.startsWith("<!")) {
|
|
641
|
+
return token;
|
|
642
|
+
}
|
|
643
|
+
const formattedLinkMatch = token.match(/^<([^|>]+)\|(.+)>$/);
|
|
644
|
+
if (formattedLinkMatch) {
|
|
645
|
+
const [, url, label] = formattedLinkMatch;
|
|
646
|
+
return `[${convertMrkdwnToMarkdown(label, options)}](<${url}>)`;
|
|
647
|
+
}
|
|
648
|
+
const autoLinkMatch = token.match(/^<([^>]+)>$/);
|
|
649
|
+
if (autoLinkMatch) {
|
|
650
|
+
return autoLinkMatch[1];
|
|
651
|
+
}
|
|
652
|
+
return token;
|
|
653
|
+
}
|
|
654
|
+
function isMrkdwnStyleMarker(character) {
|
|
655
|
+
return character === "*" || character === "_" || character === "~";
|
|
656
|
+
}
|
|
657
|
+
function isValidMrkdwnStyleOpen(text, index) {
|
|
658
|
+
const previous = text[index - 1];
|
|
659
|
+
const next = text[index + 1];
|
|
660
|
+
return isMrkdwnBoundary(previous) && !isWhitespace(next);
|
|
661
|
+
}
|
|
662
|
+
function isValidMrkdwnStyleClose(text, index) {
|
|
663
|
+
const previous = text[index - 1];
|
|
664
|
+
const next = text[index + 1];
|
|
665
|
+
return !isWhitespace(previous) && isMrkdwnBoundary(next);
|
|
666
|
+
}
|
|
667
|
+
function findClosingMrkdwnStyleMarker(text, openingIndex) {
|
|
668
|
+
const marker = text[openingIndex];
|
|
669
|
+
for (let index = openingIndex + 1; index < text.length; index++) {
|
|
670
|
+
const character = text[index];
|
|
671
|
+
if (character === "`") {
|
|
672
|
+
const closingIndex = text.indexOf("`", index + 1);
|
|
673
|
+
if (closingIndex === -1)
|
|
674
|
+
return -1;
|
|
675
|
+
index = closingIndex;
|
|
676
|
+
continue;
|
|
677
|
+
}
|
|
678
|
+
if (character === "<") {
|
|
679
|
+
const closingIndex = text.indexOf(">", index + 1);
|
|
680
|
+
if (closingIndex === -1)
|
|
681
|
+
return -1;
|
|
682
|
+
index = closingIndex;
|
|
683
|
+
continue;
|
|
684
|
+
}
|
|
685
|
+
if (character === marker && isValidMrkdwnStyleClose(text, index)) {
|
|
686
|
+
return index;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return -1;
|
|
690
|
+
}
|
|
691
|
+
function wrapConvertedMrkdwnStyle(marker, text) {
|
|
692
|
+
if (marker === "*")
|
|
693
|
+
return `**${text}**`;
|
|
694
|
+
if (marker === "_")
|
|
695
|
+
return `*${text}*`;
|
|
696
|
+
return `~${text}~`;
|
|
697
|
+
}
|
|
698
|
+
function isMrkdwnBoundary(character) {
|
|
699
|
+
if (character === undefined)
|
|
700
|
+
return true;
|
|
701
|
+
return /[\s.,!?;:()[\]{}"'<>/-]/.test(character);
|
|
702
|
+
}
|
|
703
|
+
function isWhitespace(character) {
|
|
704
|
+
return character !== undefined && /\s/.test(character);
|
|
705
|
+
}
|
|
706
|
+
function renderUserMentionAsMarkdown(userId, options) {
|
|
707
|
+
return renderNamedMention(options?.mentions?.users?.[userId] ??
|
|
708
|
+
options?.mentions?.userGroups?.[userId] ??
|
|
709
|
+
options?.mentions?.teams?.[userId], `<@${userId}>`);
|
|
710
|
+
}
|
|
711
|
+
function renderChannelMentionAsMarkdown(channelId, options) {
|
|
712
|
+
return renderNamedMention(options?.mentions?.channels?.[channelId], `<#${channelId}>`, "#");
|
|
713
|
+
}
|
|
714
|
+
function renderUserGroupMentionAsMarkdown(userGroupId, options) {
|
|
715
|
+
return renderNamedMention(options?.mentions?.userGroups?.[userGroupId], `<!subteam^${userGroupId}>`);
|
|
716
|
+
}
|
|
717
|
+
function renderTeamMentionAsMarkdown(teamId, options) {
|
|
718
|
+
return renderNamedMention(options?.mentions?.teams?.[teamId], `<!subteam^${teamId}>`);
|
|
719
|
+
}
|
|
720
|
+
function renderNamedMention(name, fallback, prefix = "@") {
|
|
721
|
+
return name ? `${prefix}${name}` : fallback;
|
|
722
|
+
}
|
|
723
|
+
function isImageElement(element) {
|
|
724
|
+
if (!element || typeof element !== "object")
|
|
725
|
+
return false;
|
|
726
|
+
const accessory = element;
|
|
727
|
+
return (accessory.type === "image" &&
|
|
728
|
+
typeof accessory.image_url === "string" &&
|
|
729
|
+
typeof accessory.alt_text === "string");
|
|
730
|
+
}
|
|
335
731
|
/**
|
|
336
732
|
* Generates a lightweight plain-text fallback from a block batch.
|
|
337
733
|
*/
|
package/dist/types.d.ts
CHANGED
|
@@ -156,4 +156,12 @@ export interface MarkdownToBlocksOptions {
|
|
|
156
156
|
detectColors?: boolean;
|
|
157
157
|
preferSectionBlocks?: boolean;
|
|
158
158
|
}
|
|
159
|
+
export interface BlocksToMarkdownOptions {
|
|
160
|
+
mentions?: {
|
|
161
|
+
users?: Record<string, string>;
|
|
162
|
+
channels?: Record<string, string>;
|
|
163
|
+
userGroups?: Record<string, string>;
|
|
164
|
+
teams?: Record<string, string>;
|
|
165
|
+
};
|
|
166
|
+
}
|
|
159
167
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,KAAK,GACd,YAAY,GACZ,WAAW,GACX,UAAU,GACV,YAAY,GACZ,YAAY,GACZ,aAAa,GACb,UAAU,CAAC;AAEd,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC;IACtB,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAGD,MAAM,MAAM,gBAAgB,GACzB,YAAY,GACZ;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAE5C,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,eAAe,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,EAAE,CAAC,YAAY,GAAG,UAAU,CAAC,EAAE,CAAC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,eAAe,GACxB,eAAe,GACf,YAAY,GACZ,oBAAoB,GACpB,aAAa,CAAC;AAEjB,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,mBAAmB,CAAC;IAC1B,QAAQ,EAAE,sBAAsB,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,wBAAwB,CAAC;IAC/B,QAAQ,EAAE,sBAAsB,EAAE,CAAC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,iBAAiB,CAAC;IACxB,QAAQ,EAAE,sBAAsB,EAAE,CAAC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,sBAAsB,GAC/B,YAAY,GACZ,YAAY,GACZ,aAAa,GACb,YAAY,GACZ,YAAY,GACZ,iBAAiB,GACjB,YAAY,GACZ,eAAe,GACf,iBAAiB,GACjB,aAAa,CAAC;AAEjB,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,WAAW,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,SAAS,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC;IACvC,KAAK,CAAC,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC7B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC/B,IAAI,EAAE,aAAa,EAAE,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAID,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,QAAQ,GAAG,YAAY,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACvC,QAAQ,CAAC,EAAE;QACV,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAClC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACpC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAC/B,CAAC;IACF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC9B"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/validator.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import type { MarkdownToBlocksOptions } from "./types";
|
|
1
|
+
import type { BlocksToMarkdownOptions, MarkdownToBlocksOptions } from "./types";
|
|
2
2
|
export declare function validateOptions(options?: MarkdownToBlocksOptions): void;
|
|
3
|
+
export declare function validateBlocksToMarkdownOptions(options?: BlocksToMarkdownOptions): void;
|
|
3
4
|
//# sourceMappingURL=validator.d.ts.map
|
package/dist/validator.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAEhF,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,uBAAuB,GAAG,IAAI,CA8CvE;AAED,wBAAgB,+BAA+B,CAC9C,OAAO,CAAC,EAAE,uBAAuB,GAC/B,IAAI,CA0CN"}
|
package/dist/validator.js
CHANGED
|
@@ -1,37 +1,54 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.validateOptions = validateOptions;
|
|
4
|
+
exports.validateBlocksToMarkdownOptions = validateBlocksToMarkdownOptions;
|
|
4
5
|
function validateOptions(options) {
|
|
5
6
|
if (!options?.mentions) {
|
|
6
7
|
return;
|
|
7
8
|
}
|
|
8
9
|
const { users, channels, userGroups, teams } = options.mentions;
|
|
9
10
|
if (users) {
|
|
10
|
-
|
|
11
|
-
if (!/^[UW][A-Z0-9]+$/.test(id)) {
|
|
12
|
-
throw new Error(`Invalid User ID for '${name}': '${id}'. Must start with U or W and contain only alphanumeric characters.`);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
11
|
+
validateIdValues(users, /^[UW][A-Z0-9]+$/, "User", (name, id) => `Invalid User ID for '${name}': '${id}'. Must start with U or W and contain only alphanumeric characters.`);
|
|
15
12
|
}
|
|
16
13
|
if (channels) {
|
|
17
|
-
|
|
18
|
-
if (!/^C[A-Z0-9]+$/.test(id)) {
|
|
19
|
-
throw new Error(`Invalid Channel ID for '${name}': '${id}'. Must start with C and contain only alphanumeric characters.`);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
14
|
+
validateIdValues(channels, /^C[A-Z0-9]+$/, "Channel", (name, id) => `Invalid Channel ID for '${name}': '${id}'. Must start with C and contain only alphanumeric characters.`);
|
|
22
15
|
}
|
|
23
16
|
if (userGroups) {
|
|
24
|
-
|
|
25
|
-
if (!/^S[A-Z0-9]+$/.test(id)) {
|
|
26
|
-
throw new Error(`Invalid User Group ID for '${name}': '${id}'. Must start with S and contain only alphanumeric characters.`);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
17
|
+
validateIdValues(userGroups, /^S[A-Z0-9]+$/, "User Group", (name, id) => `Invalid User Group ID for '${name}': '${id}'. Must start with S and contain only alphanumeric characters.`);
|
|
29
18
|
}
|
|
30
19
|
if (teams) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
20
|
+
validateIdValues(teams, /^T[A-Z0-9]+$/, "Team", (name, id) => `Invalid Team ID for '${name}': '${id}'. Must start with T and contain only alphanumeric characters.`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function validateBlocksToMarkdownOptions(options) {
|
|
24
|
+
if (!options?.mentions) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const { users, channels, userGroups, teams } = options.mentions;
|
|
28
|
+
if (users) {
|
|
29
|
+
validateIdKeys(users, /^[UW][A-Z0-9]+$/, (id) => `Invalid User ID key '${id}'. Must start with U or W and contain only alphanumeric characters.`);
|
|
30
|
+
}
|
|
31
|
+
if (channels) {
|
|
32
|
+
validateIdKeys(channels, /^C[A-Z0-9]+$/, (id) => `Invalid Channel ID key '${id}'. Must start with C and contain only alphanumeric characters.`);
|
|
33
|
+
}
|
|
34
|
+
if (userGroups) {
|
|
35
|
+
validateIdKeys(userGroups, /^S[A-Z0-9]+$/, (id) => `Invalid User Group ID key '${id}'. Must start with S and contain only alphanumeric characters.`);
|
|
36
|
+
}
|
|
37
|
+
if (teams) {
|
|
38
|
+
validateIdKeys(teams, /^T[A-Z0-9]+$/, (id) => `Invalid Team ID key '${id}'. Must start with T and contain only alphanumeric characters.`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function validateIdValues(entries, pattern, _kind, buildMessage) {
|
|
42
|
+
for (const [name, id] of Object.entries(entries)) {
|
|
43
|
+
if (!pattern.test(id)) {
|
|
44
|
+
throw new Error(buildMessage(name, id));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function validateIdKeys(entries, pattern, buildMessage) {
|
|
49
|
+
for (const id of Object.keys(entries)) {
|
|
50
|
+
if (!pattern.test(id)) {
|
|
51
|
+
throw new Error(buildMessage(id));
|
|
35
52
|
}
|
|
36
53
|
}
|
|
37
54
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "markdown-to-slack-blocks",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Convert Markdown to Slack Block Kit JSON format",
|
|
3
|
+
"version": "1.5.0",
|
|
4
|
+
"description": "Convert Markdown to Slack Block Kit JSON format and vice versa.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
7
|
"exports": {
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"node": ">=18.0.0"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"mdast-util-from-markdown": "2.0.
|
|
51
|
+
"mdast-util-from-markdown": "2.0.3",
|
|
52
52
|
"mdast-util-gfm": "3.1.0",
|
|
53
53
|
"mdast-util-to-string": "4.0.0",
|
|
54
54
|
"micromark-extension-gfm": "3.0.0"
|
|
@@ -58,6 +58,6 @@
|
|
|
58
58
|
"@types/node": "24.10.1",
|
|
59
59
|
"eslint": "9.39.1",
|
|
60
60
|
"typescript": "5.9.3",
|
|
61
|
-
"vitest": "4.
|
|
61
|
+
"vitest": "4.1.2"
|
|
62
62
|
}
|
|
63
|
-
}
|
|
63
|
+
}
|