slack-message-parser
Version:
Parser for Slack message
181 lines (180 loc) • 6.47 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
const Node_js_1 = require("./types/Node.js");
const combinator_js_1 = require("./combinator.js");
const parseBold = (0, combinator_js_1.explicit)((0, combinator_js_1.regexp)(/^\*(\S([^*\n]*?|[^*\n]*? `.*?` )[^\s*]|\S)\*(?=[\s~!#$%^)\-+={}[\];:'",.?/]|$)/, (match, _text, position, parseText) => {
const [matchedText, content] = match;
return [
{
type: Node_js_1.NodeType.Bold,
children: parseText(content),
source: matchedText,
},
position + matchedText.length,
];
}));
const parseCode = (0, combinator_js_1.explicit)((0, combinator_js_1.regexp)(/^`([^`]+?)`(?=[\s.,\])}!?\-=]|$)/, (match, _text, position) => {
const [matchedText, content] = match;
return [
{
type: Node_js_1.NodeType.Code,
text: content,
source: matchedText,
},
position + matchedText.length,
];
}));
const parsePreText = (0, combinator_js_1.explicit)((0, combinator_js_1.regexp)(/^```(\s*\S[\s\S]*?\s*)```(?=[\s.,\])}!?\-=]|$)/, (match, _text, position) => {
const [matchedText, content] = match;
return [
{
type: Node_js_1.NodeType.PreText,
text: content,
source: matchedText,
},
position + matchedText.length,
];
}));
const parseItalic = (0, combinator_js_1.explicit)((0, combinator_js_1.regexp)(/^_(\S([^_\n]*?|[^_\n]*? `.*?` )\S|\S)\_(?=[\s.,\])}!?\-=]|$)/, (match, _text, position, parseText) => {
const [matchedText, content] = match;
return [
{
type: Node_js_1.NodeType.Italic,
children: parseText(content),
source: matchedText,
},
position + matchedText.length,
];
}));
const parseStrike = (0, combinator_js_1.explicit)((0, combinator_js_1.regexp)(/^~(\S([^~\n]*?|[^~\n]*? `.*?` )\S|\S)\~(?=[\s.,\])}!?\-=]|$)/, (match, _text, position, parseText) => {
const [matchedText, content] = match;
return [
{
type: Node_js_1.NodeType.Strike,
children: parseText(content),
source: matchedText,
},
position + matchedText.length,
];
}));
const parseQuoteLine = (0, combinator_js_1.topOfLine)((0, combinator_js_1.regexp)(/^>(.*)(\n>|\n|$)/, (match, _text, position, parseText) => {
const [matchedText, content] = match;
const repeatedGt = content.match(/^((>)+)(.*)$/);
// If the next line is also starts with ">", do not include the character
// (simulating RegExp's non-capturing group)
const source = matchedText.replace(/\n>$/, "\n");
// `source` and `matchedText` are same unless the next line starts with ">"
// due to the above line removes the ">".
const isNextLineStartsWithGt = source !== matchedText;
return [
{
type: Node_js_1.NodeType.Quote,
children: repeatedGt
? [
{
type: Node_js_1.NodeType.Text,
text: repeatedGt[1],
source: repeatedGt[1],
},
...parseText(repeatedGt[3]),
]
// Only the last LF could be a terminator character of quote.
// Non-last LFs should be parsed as well as `content`.
: parseText(isNextLineStartsWithGt ? content + "\n" : content),
source,
},
position + source.length,
];
}));
const parseQuote = (0, combinator_js_1.or)([
(0, combinator_js_1.topOfLine)((0, combinator_js_1.regexp)(/^>>>([\s\S]+)$/, (match, _text, position, parseText) => {
const [matchedText, content] = match;
return [
{
type: Node_js_1.NodeType.Quote,
children: parseText(content),
source: matchedText,
},
position + matchedText.length,
];
})),
(0, combinator_js_1.oneOrMore)(parseQuoteLine, (quotes) => {
return {
type: Node_js_1.NodeType.Quote,
children: quotes.map((quote) => quote.children).flat(),
source: quotes.map((quote) => quote.source).join(""),
};
}),
]);
const parseEmoji = (0, combinator_js_1.regexp)(/^:([^:<`*#@!\s()$%]+):(:(skin-tone-.+?):)?/, (match, _text, position) => {
const [matchedText, name, _, variation] = match;
return [
{
type: Node_js_1.NodeType.Emoji,
name,
variation,
source: matchedText,
},
position + matchedText.length,
];
});
const parseLink = (0, combinator_js_1.regexp)(/^<([^\s<>][^\n<>]*?)(\|([^<>]+?))?>/, (match, _text, position, parseText) => {
const [matchedText, link, _, label] = match;
const nextPosition = position + matchedText.length;
const labelNodes = label ? parseText(label) : undefined;
switch (link.charAt(0)) {
case "@":
return [
{
type: Node_js_1.NodeType.UserLink,
userID: link.slice(1),
label: labelNodes,
source: matchedText,
},
nextPosition,
];
case "#":
return [
{
type: Node_js_1.NodeType.ChannelLink,
channelID: link.slice(1),
label: labelNodes,
source: matchedText,
},
nextPosition,
];
case "!": {
const [commandName, ...args] = link.slice(1).split("^");
return [
{
type: Node_js_1.NodeType.Command,
name: commandName,
arguments: args,
label: labelNodes,
source: matchedText,
},
nextPosition,
];
}
default:
return [
{
type: Node_js_1.NodeType.URL,
url: link,
label: labelNodes,
source: matchedText,
},
nextPosition,
];
}
});
exports.default = (0, combinator_js_1.or)([
parseBold,
parsePreText,
parseCode,
parseEmoji,
parseItalic,
parseQuote,
parseLink,
parseStrike,
]);
;