New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@chayns-components/format

Package Overview
Dependencies
Maintainers
0
Versions
30
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@chayns-components/format - npm Package Compare versions

Comparing version 5.0.0-beta.651 to 5.0.0-beta.674

lib/cjs/types/format.js

4

lib/cjs/utils/escape.js

@@ -6,9 +6,7 @@ "use strict";

});
exports.unescapeSquareBrackets = exports.escapeHtmlInText = exports.MESSAGE_CONVERSION_LINE_BREAK_ESCAPED = exports.MESSAGE_CONVERSION_LINE_BREAK = void 0;
exports.escapeHtmlInText = exports.MESSAGE_CONVERSION_LINE_BREAK_ESCAPED = exports.MESSAGE_CONVERSION_LINE_BREAK = void 0;
const escapeHtmlInText = text => text.replace(/</g, '&lt;').replace(/>/g, '&gt;');
exports.escapeHtmlInText = escapeHtmlInText;
const unescapeSquareBrackets = text => text.replaceAll('&#91;', '[').replaceAll('&#93;', ']');
exports.unescapeSquareBrackets = unescapeSquareBrackets;
const MESSAGE_CONVERSION_LINE_BREAK = exports.MESSAGE_CONVERSION_LINE_BREAK = '<br is-replaced-linebreak>';
const MESSAGE_CONVERSION_LINE_BREAK_ESCAPED = exports.MESSAGE_CONVERSION_LINE_BREAK_ESCAPED = escapeHtmlInText(MESSAGE_CONVERSION_LINE_BREAK);
//# sourceMappingURL=escape.js.map

@@ -7,3 +7,3 @@ "use strict";

exports.findFirstBBCode = findFirstBBCode;
const BB_REGEX = /\[([a-zA-Z0-9_]*)(.*?)\](.*?)\[\/\1\]/s;
const BB_REGEX = /(\[([a-zA-Z0-9_]*)(.*?)\])(.*?)(\[\/\2\])/s;
// Also matches "\" before quote to fix button for voucher messages

@@ -18,7 +18,7 @@ const PARAMETER_REGEX = /([\w]*?)=\\?["„](.*?)["“]/g;

if (matches !== null) {
const [fullMatch, tag, params, content] = matches;
const [fullMatch, openingTag, tag, params, content, closingTag] = matches;
const {
index
} = matches;
if (fullMatch === undefined || tag === undefined || params === undefined || content === undefined) return null;
if (fullMatch === undefined || tag === undefined || params === undefined || content === undefined || openingTag === undefined || closingTag === undefined) return null;
const parameters = {};

@@ -38,3 +38,5 @@ let match = null;

content,
index
index,
openingTag,
closingTag
};

@@ -41,0 +43,0 @@ }

@@ -6,3 +6,3 @@ "use strict";

});
exports.parseBBCode = void 0;
exports.unescapeBBCodeSquareBrackets = exports.parseBBCode = exports.escapeBBCodeSquareBrackets = void 0;
var _findBBCode = require("./findBBCode");

@@ -12,3 +12,2 @@ const BB_CODE_HTML_TAG_PREFIX = 'bb-code-';

const INLINE_LEVEL_TAGS = ['b', 'strong', 'i', 'em', 'u', 's', 'span', 'img'];
const HTML_CODE_PATTERN = /(?:<code>|<code class="inline-code">)[\s\S]*?<\/code>/;
// Parses BB-Code to HTML recursively.

@@ -19,5 +18,5 @@ // When justEscapeSquareBrackets is true, square brackets are escaped to prevent conflicts between markdown and BB Code.

const {
justEscapeSquareBrackets = false,
customBlockLevelBBCodeTags: customBlockLevelTags = [],
customInlineLevelBBCodeTags: customInlineLevelTags = []
customInlineLevelBBCodeTags: customInlineLevelTags = [],
justEscapeSquareBrackets = false
} = options || {};

@@ -30,3 +29,2 @@ let html = text;

const htmlToParse = html.slice(parseBehindIndex);
const firstCodeElementMatch = HTML_CODE_PATTERN.exec(htmlToParse);
const firstBBCodeMatch = (0, _findBBCode.findFirstBBCode)(htmlToParse);

@@ -38,10 +36,2 @@

}
// Prevents bb-code parsing within code block.
if (firstCodeElementMatch && firstBBCodeMatch && firstCodeElementMatch.index < firstBBCodeMatch.index) {
// If a code block is found before a BB-Code tag, BB-Code parsing continues behind the code block.
parseBehindIndex += firstCodeElementMatch.index + firstCodeElementMatch[0].length;
// eslint-disable-next-line no-continue
continue;
}
const {

@@ -51,3 +41,5 @@ content,

parameters,
index
index,
openingTag,
closingTag
} = firstBBCodeMatch;

@@ -67,31 +59,35 @@ const Tag = firstBBCodeMatch.tag.toLowerCase();

// Converts BB-Code tag's content before converting itself, because it may contain other BB-Codes.
let parsedContent = parseBBCode(content);
let parsedContent = parseBBCode(content, options);
if (justEscapeSquareBrackets) {
const indexOfFullMatch = html.indexOf(fullMatch);
const escapedOpeningTag = escapeBBCodeSquareBrackets(openingTag);
const escapedClosingTag = escapeBBCodeSquareBrackets(closingTag);
// Removes leading and trailing line-breaks from within bb code elements, to prevent unwanted spacing.
if (!justEscapeSquareBrackets) {
// Removes leading and trailing line-breaks from within bb code elements, to prevent unwanted spacing.
// This needs to be done before formatting Markdown, so the Markdown formatting doesn't interpret line breaks unexpectedly.
parsedContent = parsedContent.replace(/^\n+|\n+$/g, '');
}
const indexOfFullMatch = html.indexOf(fullMatch);
let htmlAfterTag = html.slice(indexOfFullMatch + fullMatch.length);
// Removes leading line-break (ONE, NOT ALL) after block level elements, to prevent unwanted spacing.
if (!justEscapeSquareBrackets && isBlockLevelTag) {
htmlAfterTag = htmlAfterTag.replace(/^\n/, '');
}
// Simply escapes the square brackets of the BB-Code opening and closing tag.
html = html.slice(0, indexOfFullMatch) + escapedOpeningTag + parsedContent + escapedClosingTag + html.slice(indexOfFullMatch + fullMatch.length);
// Use escaped square brackets to prevent conflicts between markdown and BB Code.
const openTag = justEscapeSquareBrackets ? '&#91;' : '<';
const closeTag = justEscapeSquareBrackets ? '&#93;' : '>';
// Continues parsing behind the parsed bb-code.
parseBehindIndex = indexOfFullMatch + escapedOpeningTag.length + parsedContent.length + escapedClosingTag.length;
} else {
const indexOfFullMatch = html.indexOf(fullMatch);
let htmlAfterTag = html.slice(indexOfFullMatch + fullMatch.length);
// TODO Don't alter content of bb-code tags when justEscapeSquareBrackets is true.
// This is necessary to preserve whitespaces in bb-code tags within code blocks.
// Removes leading line-break (ONE, NOT ALL) after block level elements, to prevent unwanted spacing.
if (isBlockLevelTag) {
htmlAfterTag = htmlAfterTag.replace(/^\n/, '');
}
const isCustomTag = [...customBlockLevelTags, ...customInlineLevelTags].includes(Tag);
const htmlTag = isCustomTag ? `${BB_CODE_HTML_TAG_PREFIX}${Tag}` : Tag;
const openingHtmlTag = `<${htmlTag}${Object.entries(parameters).length > 0 ? ' ' : ''}${Object.entries(parameters).map(([key, value]) => `${key}="${value}"`).join(' ')}>`;
const closingHtmlTag = `</${htmlTag}>`;
const element = Tag === 'img' ? openingHtmlTag : openingHtmlTag + parsedContent + closingHtmlTag;
html = `${html.slice(0, indexOfFullMatch)}${element}${htmlAfterTag}`;
const isCustomTag = [...customBlockLevelTags, ...customInlineLevelTags].includes(Tag);
const htmlTag = !justEscapeSquareBrackets && isCustomTag ? `${BB_CODE_HTML_TAG_PREFIX}${Tag}` : Tag;
const openingTag = `${openTag}${htmlTag}${Object.entries(parameters).length > 0 ? ' ' : ''}${Object.entries(parameters).map(([key, value]) => `${key}="${value}"`).join(' ')}${closeTag}`;
const closingTag = `${openTag}/${htmlTag}${closeTag}`;
html = html.slice(0, indexOfFullMatch) + openingTag + parsedContent + closingTag + htmlAfterTag;
// Continues parsing behind the parsed bb-code.
parseBehindIndex = indexOfFullMatch + openingTag.length + parsedContent.length + closingTag.length;
// Continues parsing behind the parsed bb-code.
parseBehindIndex = indexOfFullMatch + element.length;
}
}

@@ -101,2 +97,6 @@ return html;

exports.parseBBCode = parseBBCode;
const escapeBBCodeSquareBrackets = text => text.replaceAll('[', '&zwj;[&zwj;').replaceAll(']', '&zwj;]&zwj;');
exports.escapeBBCodeSquareBrackets = escapeBBCodeSquareBrackets;
const unescapeBBCodeSquareBrackets = text => text.replaceAll('&zwj;[&zwj;', '[').replaceAll('&zwj;]&zwj;', ']');
exports.unescapeBBCodeSquareBrackets = unescapeBBCodeSquareBrackets;
//# sourceMappingURL=formatBBCode.js.map

@@ -10,3 +10,2 @@ "use strict";

var _formatMarkdown = require("./markdown/formatMarkdown");
var _formatMarkdownTable = require("./markdown/formatMarkdownTable");
// This function takes a string and returns formatted html as a string.

@@ -21,5 +20,3 @@ const formatStringToHtml = (string, options) => {

const {
escapeHtml: escapeHtmlOption = false,
parseMarkdown: parseMarkdownOption = true,
parseMarkdownTables: parseMarkdownTablesOption = false,
parseBBCode: parseBBCodeOption = false,

@@ -31,30 +28,36 @@ customInlineLevelBBCodeTags = [],

// Escapes HTML.
if (escapeHtmlOption) {
formattedString = (0, _escape.escapeHtmlInText)(formattedString);
}
// Escape BB-Code square brackets, to prevent conflicts between markdown and BB Code.
/* Conflict example:
When Sidekick detects a function call as an entity through NER, then the following text is returned.
'[nerReplace <params>]function[/nerReplace](<params>)'
Because '[/nerReplace](<params>)' is a valid Markdown link, the Markdown parser would interpret it as a link
and thus prevent the BB-Code parser from recognizing the BB-Code. Parsing the BB-Code first would prevent this
issue. Unfortunately the Markdown parser doesn't support this.
*/
const shouldTemporarilyEscapeBBCodeBrackets = parseMarkdownOption && parseBBCodeOption;
if (shouldTemporarilyEscapeBBCodeBrackets) {
// Needs to get the tables before escaping html and parsing bb-code, so the original content can be extracted.
const tables = [];
if (parseMarkdownOption) {
try {
formattedString = (0, _formatBBCode.parseBBCode)(formattedString, {
justEscapeSquareBrackets: true
});
tables.push(...(0, _formatMarkdown.getMarkdownTables)(formattedString));
} catch (error) {
console.warn('[@chayns-components/format] Warning: Failed to escape bb-code brackets', error);
console.warn('[@chayns-components/format] Warning: Failed to get markdown tables', error);
}
}
// Parses markdown to HTML. Markdown tables are parsed separately.
// Escape HTML entities.
formattedString = (0, _escape.escapeHtmlInText)(formattedString);
// Escape BB-Code, to prevent conflicts between markdown and bb-code. Specifically [b]test[/b]() would be a problem, since markdown interprets parts of this as a link.
// Parses markdown to HTML.
if (parseMarkdownOption) {
try {
formattedString = (0, _formatMarkdown.parseMarkdown)(formattedString);
if (parseBBCodeOption) {
// Escapes BB-Code brackets.
formattedString = (0, _formatBBCode.parseBBCode)(formattedString, {
customInlineLevelBBCodeTags,
customBlockLevelBBCodeTags,
justEscapeSquareBrackets: true
});
}
formattedString = (0, _formatMarkdown.parseMarkdown)(formattedString, parseBBCodeOption);
// Remove trailing \n
formattedString = formattedString.replace(/\n$/, '');
if (parseBBCodeOption) {
// Unescapes BB-Code brackets.
formattedString = (0, _formatBBCode.unescapeBBCodeSquareBrackets)(formattedString);
}
} catch (error) {

@@ -64,19 +67,4 @@ console.warn('[@chayns-components/format] Warning: Failed to parse markdown', error);

}
const tables = [];
// Parses markdown tables to HTML. Also returns the tables content as an array, to allow further processing.
if (parseMarkdownTablesOption) {
try {
const result = (0, _formatMarkdownTable.parseMarkdownTables)(formattedString);
formattedString = result.html;
tables.push(...result.tables);
} catch (error) {
console.warn('[@chayns-components/format] Warning: Failed to parse markdown tables', error);
}
}
// Unescapes BB-Code square brackets, to allow parsing of BB-Code.
if (shouldTemporarilyEscapeBBCodeBrackets) {
formattedString = (0, _escape.unescapeSquareBrackets)(formattedString);
}
// Parses BB-Code to HTML.
if (parseBBCodeOption) {

@@ -86,5 +74,5 @@ try {

customInlineLevelBBCodeTags,
customBlockLevelBBCodeTags,
justEscapeSquareBrackets: false
customBlockLevelBBCodeTags
});
formattedString = (0, _formatBBCode.unescapeBBCodeSquareBrackets)(formattedString);
} catch (error) {

@@ -91,0 +79,0 @@ console.warn('[@chayns-components/format] Warning: Failed to parse bb-code', error);

@@ -6,38 +6,153 @@ "use strict";

});
exports.parseMarkdown = void 0;
var _commonmark = require("commonmark");
var _escape = require("../../escape");
var _InternalHTMLRenderer = _interopRequireDefault(require("./InternalHTMLRenderer"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
exports.parseMarkdown = exports.getMarkdownTables = void 0;
var _marked = require("marked");
var _sync = require("csv-stringify/browser/esm/sync");
var _formatBBCode = require("../bb-code/formatBBCode");
// eslint-disable-next-line import/extensions,import/no-unresolved
const inlineCodeRule = /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/;
const inlineTextRule = /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<![`*_]|\b_|$)|[^ ](?= {2,}\n)))/;
const TABLE_ID_PREFIX = 'formatted-table-';
/*
The marked Pipeline, including tokenizer, renderer and hooks are explained here:
https://marked.js.org/using_pro
*/
const tokenizer = {
// Codespan Tokenizer is overwritten to prevent html escaping, since html is already escaped.
// The function is copied from marked.js and slightly modified: https://github.com/markedjs/marked/blob/42954aaba960b6f815b24ec0d39da464960e4ec9/src/Tokenizer.ts#L749
codespan(src) {
const cap = inlineCodeRule.exec(src);
if (cap) {
let text = cap[2].replace(/\n/g, ' ');
const hasNonSpaceChars = /[^ ]/.test(text);
const hasSpaceCharsOnBothEnds = /^ /.test(text) && / $/.test(text);
if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {
text = text.substring(1, text.length - 1);
}
return {
type: 'codespan',
raw: cap[0],
text
};
}
return undefined;
},
lheading() {
return undefined;
},
url() {
return undefined;
},
// inlineText is overwritten to prevent html escaping, specifically since quote characters are escaped, which breaks the attributes of bb-code elements.
// The function is copied from marked.js and slightly modified: https://github.com/markedjs/marked/blob/42954aaba960b6f815b24ec0d39da464960e4ec9/src/Tokenizer.ts#L854
inlineText(src) {
const cap = inlineTextRule.exec(src);
if (cap) {
return {
type: 'text',
raw: cap[0],
text: cap[0]
};
}
return undefined;
}
};
const renderer = {
// Code Renderer is overwritten to prevent html escaping, since html is already escaped.
// The function is copied from marked.js and slightly modified: https://github.com/markedjs/marked/blob/42954aaba960b6f815b24ec0d39da464960e4ec9/src/Renderer.ts#L24
code(text, lang) {
var _match;
const langString = (_match = (lang || '').match(/^\S*/)) === null || _match === void 0 ? void 0 : _match[0];
const code = `${text.replace(/\n$/, '')}`;
if (!langString) {
return `<pre><code>${code}</code></pre>\n`;
}
return `<pre><code class="language-${langString}">${code}</code></pre>\n`;
},
// Replaces the checkbox input elements with markdown checkboxes.
// This is the easiest way to prevent the formatting of markdown checkboxes in lists.
// This can modify the input string slightly, since the capitalization of the checkbox can be lost.
// If a user types '- [X]' it will be replaced with '- [x]' => the capitalization is lost.
checkbox({
checked
}) {
return checked ? '[x]' : '[ ]';
}
};
const postprocess = html => {
let tableIndex = 0;
// Assigns ids to tables.
const modifiedString = html.replace(/(<table>)/g, () => {
const result = `<table id="${TABLE_ID_PREFIX}${tableIndex}">`;
tableIndex++;
return result;
});
return modifiedString;
};
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
_marked.marked.use({
tokenizer,
renderer,
hooks: {
postprocess
}
});
const parseMarkdown = text => {
let newText = text;
// Parses markdown following the Github Flavored Markdown specification.
// The tokenizer and renderer are slightly modified to prevent html escaping in code block and inline code.
const parseMarkdown = (text, parseBBCode) => _marked.marked.parse(text, {
walkTokens: token => {
if (parseBBCode && (token.type === 'codespan' || token.type === 'code')) {
// eslint-disable-next-line no-param-reassign
token.text = (0, _formatBBCode.escapeBBCodeSquareBrackets)(token.text);
}
}
});
// Markdown has its own line break handling. For that reason, we need to replace line breaks with a custom element.
// In this case I chose a custom <br> Tag.
// Since commonmark doesn't parse markdown in lines with html, the custom <br> Tag needs to be in its own line.
// Since there are issues, when the <br> Tag + \n is followed by text, there needs to be a second line break.
// Thus, we replace \n with \n<br>\n\n.
newText = newText.replaceAll(/\n/gm, `\n${_escape.MESSAGE_CONVERSION_LINE_BREAK}\n\n`);
const commonmarkParser = new _commonmark.Parser();
// TODO Check if esc needs to be passed to InternalHTMLRenderer.
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
// @ts-ignore
const internalHTMLRenderer = new _InternalHTMLRenderer.default({
esc: _escape.escapeHtmlInText
// It is important that, &amp; is replaced lastly to prevent double escaping.
exports.parseMarkdown = parseMarkdown;
const unescapeHtml = text => text.replaceAll('&lt;', '<').replaceAll('&gt;', '>').replaceAll('&amp;', '&');
const getMarkdownTables = text => {
const tableTokens = [];
_marked.marked.parse(text, {
walkTokens: token => {
if (token.type === 'table') {
tableTokens.push(token);
}
}
});
// Converts markdown to HTML.
const parsedText = commonmarkParser.parse(newText);
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
newText = internalHTMLRenderer.render(parsedText);
// The Linebreak handling of markdown is ignored, by removing \n. Then the custom <br> Tags are converted back to \n.
newText = newText.replaceAll(/\n/gm, '');
newText = newText.replaceAll(_escape.MESSAGE_CONVERSION_LINE_BREAK, '\n').replaceAll(_escape.MESSAGE_CONVERSION_LINE_BREAK_ESCAPED, '\n');
return newText;
const tables = [];
tableTokens.forEach((tableToken, index) => {
var _tableToken$header, _tableToken$rows;
const tableArray = [];
if (((_tableToken$header = tableToken.header) === null || _tableToken$header === void 0 ? void 0 : _tableToken$header.length) > 0) {
const rowArray = [];
tableToken.header.forEach(header => {
rowArray.push(unescapeHtml(header.text));
});
tableArray.push(rowArray);
}
if (((_tableToken$rows = tableToken.rows) === null || _tableToken$rows === void 0 ? void 0 : _tableToken$rows.length) > 0) {
tableToken.rows.forEach(row => {
const rowArray = [];
row.forEach(cell => {
rowArray.push(unescapeHtml(cell.text));
});
tableArray.push(rowArray);
});
}
const csv = (0, _sync.stringify)(tableArray || []);
tables.push({
raw: unescapeHtml(tableToken.raw),
csv,
id: `${TABLE_ID_PREFIX}${index}`
});
});
return tables;
};
exports.parseMarkdown = parseMarkdown;
exports.getMarkdownTables = getMarkdownTables;
//# sourceMappingURL=formatMarkdown.js.map
export const escapeHtmlInText = text => text.replace(/</g, '&lt;').replace(/>/g, '&gt;');
export const unescapeSquareBrackets = text => text.replaceAll('&#91;', '[').replaceAll('&#93;', ']');
export const MESSAGE_CONVERSION_LINE_BREAK = '<br is-replaced-linebreak>';
export const MESSAGE_CONVERSION_LINE_BREAK_ESCAPED = escapeHtmlInText(MESSAGE_CONVERSION_LINE_BREAK);
//# sourceMappingURL=escape.js.map

@@ -1,2 +0,2 @@

const BB_REGEX = /\[([a-zA-Z0-9_]*)(.*?)\](.*?)\[\/\1\]/s;
const BB_REGEX = /(\[([a-zA-Z0-9_]*)(.*?)\])(.*?)(\[\/\2\])/s;
// Also matches "\" before quote to fix button for voucher messages

@@ -11,7 +11,7 @@ const PARAMETER_REGEX = /([\w]*?)=\\?["„](.*?)["“]/g;

if (matches !== null) {
const [fullMatch, tag, params, content] = matches;
const [fullMatch, openingTag, tag, params, content, closingTag] = matches;
const {
index
} = matches;
if (fullMatch === undefined || tag === undefined || params === undefined || content === undefined) return null;
if (fullMatch === undefined || tag === undefined || params === undefined || content === undefined || openingTag === undefined || closingTag === undefined) return null;
const parameters = {};

@@ -31,3 +31,5 @@ let match = null;

content,
index
index,
openingTag,
closingTag
};

@@ -34,0 +36,0 @@ }

@@ -5,3 +5,2 @@ import { findFirstBBCode } from './findBBCode';

const INLINE_LEVEL_TAGS = ['b', 'strong', 'i', 'em', 'u', 's', 'span', 'img'];
const HTML_CODE_PATTERN = /(?:<code>|<code class="inline-code">)[\s\S]*?<\/code>/;
// Parses BB-Code to HTML recursively.

@@ -12,5 +11,5 @@ // When justEscapeSquareBrackets is true, square brackets are escaped to prevent conflicts between markdown and BB Code.

const {
justEscapeSquareBrackets = false,
customBlockLevelBBCodeTags: customBlockLevelTags = [],
customInlineLevelBBCodeTags: customInlineLevelTags = []
customInlineLevelBBCodeTags: customInlineLevelTags = [],
justEscapeSquareBrackets = false
} = options || {};

@@ -23,3 +22,2 @@ let html = text;

const htmlToParse = html.slice(parseBehindIndex);
const firstCodeElementMatch = HTML_CODE_PATTERN.exec(htmlToParse);
const firstBBCodeMatch = findFirstBBCode(htmlToParse);

@@ -31,10 +29,2 @@

}
// Prevents bb-code parsing within code block.
if (firstCodeElementMatch && firstBBCodeMatch && firstCodeElementMatch.index < firstBBCodeMatch.index) {
// If a code block is found before a BB-Code tag, BB-Code parsing continues behind the code block.
parseBehindIndex += firstCodeElementMatch.index + firstCodeElementMatch[0].length;
// eslint-disable-next-line no-continue
continue;
}
const {

@@ -44,3 +34,5 @@ content,

parameters,
index
index,
openingTag,
closingTag
} = firstBBCodeMatch;

@@ -60,37 +52,43 @@ const Tag = firstBBCodeMatch.tag.toLowerCase();

// Converts BB-Code tag's content before converting itself, because it may contain other BB-Codes.
let parsedContent = parseBBCode(content);
let parsedContent = parseBBCode(content, options);
if (justEscapeSquareBrackets) {
const indexOfFullMatch = html.indexOf(fullMatch);
const escapedOpeningTag = escapeBBCodeSquareBrackets(openingTag);
const escapedClosingTag = escapeBBCodeSquareBrackets(closingTag);
// Removes leading and trailing line-breaks from within bb code elements, to prevent unwanted spacing.
if (!justEscapeSquareBrackets) {
// Removes leading and trailing line-breaks from within bb code elements, to prevent unwanted spacing.
// This needs to be done before formatting Markdown, so the Markdown formatting doesn't interpret line breaks unexpectedly.
parsedContent = parsedContent.replace(/^\n+|\n+$/g, '');
}
const indexOfFullMatch = html.indexOf(fullMatch);
let htmlAfterTag = html.slice(indexOfFullMatch + fullMatch.length);
// Removes leading line-break (ONE, NOT ALL) after block level elements, to prevent unwanted spacing.
if (!justEscapeSquareBrackets && isBlockLevelTag) {
htmlAfterTag = htmlAfterTag.replace(/^\n/, '');
}
// Simply escapes the square brackets of the BB-Code opening and closing tag.
html = html.slice(0, indexOfFullMatch) + escapedOpeningTag + parsedContent + escapedClosingTag + html.slice(indexOfFullMatch + fullMatch.length);
// Use escaped square brackets to prevent conflicts between markdown and BB Code.
const openTag = justEscapeSquareBrackets ? '&#91;' : '<';
const closeTag = justEscapeSquareBrackets ? '&#93;' : '>';
// Continues parsing behind the parsed bb-code.
parseBehindIndex = indexOfFullMatch + escapedOpeningTag.length + parsedContent.length + escapedClosingTag.length;
} else {
const indexOfFullMatch = html.indexOf(fullMatch);
let htmlAfterTag = html.slice(indexOfFullMatch + fullMatch.length);
// TODO Don't alter content of bb-code tags when justEscapeSquareBrackets is true.
// This is necessary to preserve whitespaces in bb-code tags within code blocks.
// Removes leading line-break (ONE, NOT ALL) after block level elements, to prevent unwanted spacing.
if (isBlockLevelTag) {
htmlAfterTag = htmlAfterTag.replace(/^\n/, '');
}
const isCustomTag = [...customBlockLevelTags, ...customInlineLevelTags].includes(Tag);
const htmlTag = isCustomTag ? `${BB_CODE_HTML_TAG_PREFIX}${Tag}` : Tag;
const openingHtmlTag = `<${htmlTag}${Object.entries(parameters).length > 0 ? ' ' : ''}${Object.entries(parameters).map(_ref => {
let [key, value] = _ref;
return `${key}="${value}"`;
}).join(' ')}>`;
const closingHtmlTag = `</${htmlTag}>`;
const element = Tag === 'img' ? openingHtmlTag : openingHtmlTag + parsedContent + closingHtmlTag;
html = `${html.slice(0, indexOfFullMatch)}${element}${htmlAfterTag}`;
const isCustomTag = [...customBlockLevelTags, ...customInlineLevelTags].includes(Tag);
const htmlTag = !justEscapeSquareBrackets && isCustomTag ? `${BB_CODE_HTML_TAG_PREFIX}${Tag}` : Tag;
const openingTag = `${openTag}${htmlTag}${Object.entries(parameters).length > 0 ? ' ' : ''}${Object.entries(parameters).map(_ref => {
let [key, value] = _ref;
return `${key}="${value}"`;
}).join(' ')}${closeTag}`;
const closingTag = `${openTag}/${htmlTag}${closeTag}`;
html = html.slice(0, indexOfFullMatch) + openingTag + parsedContent + closingTag + htmlAfterTag;
// Continues parsing behind the parsed bb-code.
parseBehindIndex = indexOfFullMatch + openingTag.length + parsedContent.length + closingTag.length;
// Continues parsing behind the parsed bb-code.
parseBehindIndex = indexOfFullMatch + element.length;
}
}
return html;
};
export const escapeBBCodeSquareBrackets = text => text.replaceAll('[', '&zwj;[&zwj;').replaceAll(']', '&zwj;]&zwj;');
export const unescapeBBCodeSquareBrackets = text => text.replaceAll('&zwj;[&zwj;', '[').replaceAll('&zwj;]&zwj;', ']');
//# sourceMappingURL=formatBBCode.js.map

@@ -1,5 +0,4 @@

import { escapeHtmlInText, unescapeSquareBrackets } from '../escape';
import { parseBBCode } from './bb-code/formatBBCode';
import { parseMarkdown } from './markdown/formatMarkdown';
import { parseMarkdownTables } from './markdown/formatMarkdownTable';
import { escapeHtmlInText } from '../escape';
import { parseBBCode, unescapeBBCodeSquareBrackets } from './bb-code/formatBBCode';
import { getMarkdownTables, parseMarkdown } from './markdown/formatMarkdown';
// This function takes a string and returns formatted html as a string.

@@ -14,5 +13,3 @@ export const formatStringToHtml = (string, options) => {

const {
escapeHtml: escapeHtmlOption = false,
parseMarkdown: parseMarkdownOption = true,
parseMarkdownTables: parseMarkdownTablesOption = false,
parseBBCode: parseBBCodeOption = false,

@@ -24,30 +21,36 @@ customInlineLevelBBCodeTags = [],

// Escapes HTML.
if (escapeHtmlOption) {
formattedString = escapeHtmlInText(formattedString);
}
// Escape BB-Code square brackets, to prevent conflicts between markdown and BB Code.
/* Conflict example:
When Sidekick detects a function call as an entity through NER, then the following text is returned.
'[nerReplace <params>]function[/nerReplace](<params>)'
Because '[/nerReplace](<params>)' is a valid Markdown link, the Markdown parser would interpret it as a link
and thus prevent the BB-Code parser from recognizing the BB-Code. Parsing the BB-Code first would prevent this
issue. Unfortunately the Markdown parser doesn't support this.
*/
const shouldTemporarilyEscapeBBCodeBrackets = parseMarkdownOption && parseBBCodeOption;
if (shouldTemporarilyEscapeBBCodeBrackets) {
// Needs to get the tables before escaping html and parsing bb-code, so the original content can be extracted.
const tables = [];
if (parseMarkdownOption) {
try {
formattedString = parseBBCode(formattedString, {
justEscapeSquareBrackets: true
});
tables.push(...getMarkdownTables(formattedString));
} catch (error) {
console.warn('[@chayns-components/format] Warning: Failed to escape bb-code brackets', error);
console.warn('[@chayns-components/format] Warning: Failed to get markdown tables', error);
}
}
// Parses markdown to HTML. Markdown tables are parsed separately.
// Escape HTML entities.
formattedString = escapeHtmlInText(formattedString);
// Escape BB-Code, to prevent conflicts between markdown and bb-code. Specifically [b]test[/b]() would be a problem, since markdown interprets parts of this as a link.
// Parses markdown to HTML.
if (parseMarkdownOption) {
try {
formattedString = parseMarkdown(formattedString);
if (parseBBCodeOption) {
// Escapes BB-Code brackets.
formattedString = parseBBCode(formattedString, {
customInlineLevelBBCodeTags,
customBlockLevelBBCodeTags,
justEscapeSquareBrackets: true
});
}
formattedString = parseMarkdown(formattedString, parseBBCodeOption);
// Remove trailing \n
formattedString = formattedString.replace(/\n$/, '');
if (parseBBCodeOption) {
// Unescapes BB-Code brackets.
formattedString = unescapeBBCodeSquareBrackets(formattedString);
}
} catch (error) {

@@ -57,19 +60,4 @@ console.warn('[@chayns-components/format] Warning: Failed to parse markdown', error);

}
const tables = [];
// Parses markdown tables to HTML. Also returns the tables content as an array, to allow further processing.
if (parseMarkdownTablesOption) {
try {
const result = parseMarkdownTables(formattedString);
formattedString = result.html;
tables.push(...result.tables);
} catch (error) {
console.warn('[@chayns-components/format] Warning: Failed to parse markdown tables', error);
}
}
// Unescapes BB-Code square brackets, to allow parsing of BB-Code.
if (shouldTemporarilyEscapeBBCodeBrackets) {
formattedString = unescapeSquareBrackets(formattedString);
}
// Parses BB-Code to HTML.
if (parseBBCodeOption) {

@@ -79,5 +67,5 @@ try {

customInlineLevelBBCodeTags,
customBlockLevelBBCodeTags,
justEscapeSquareBrackets: false
customBlockLevelBBCodeTags
});
formattedString = unescapeBBCodeSquareBrackets(formattedString);
} catch (error) {

@@ -84,0 +72,0 @@ console.warn('[@chayns-components/format] Warning: Failed to parse bb-code', error);

@@ -1,33 +0,147 @@

import { Parser } from 'commonmark';
import { escapeHtmlInText, MESSAGE_CONVERSION_LINE_BREAK, MESSAGE_CONVERSION_LINE_BREAK_ESCAPED } from '../../escape';
import { marked } from 'marked';
// eslint-disable-next-line import/extensions,import/no-unresolved
import { stringify } from 'csv-stringify/browser/esm/sync';
import { escapeBBCodeSquareBrackets } from '../bb-code/formatBBCode';
const inlineCodeRule = /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/;
const inlineTextRule = /^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<![`*_]|\b_|$)|[^ ](?= {2,}\n)))/;
const TABLE_ID_PREFIX = 'formatted-table-';
/*
The marked Pipeline, including tokenizer, renderer and hooks are explained here:
https://marked.js.org/using_pro
*/
const tokenizer = {
// Codespan Tokenizer is overwritten to prevent html escaping, since html is already escaped.
// The function is copied from marked.js and slightly modified: https://github.com/markedjs/marked/blob/42954aaba960b6f815b24ec0d39da464960e4ec9/src/Tokenizer.ts#L749
codespan(src) {
const cap = inlineCodeRule.exec(src);
if (cap) {
let text = cap[2].replace(/\n/g, ' ');
const hasNonSpaceChars = /[^ ]/.test(text);
const hasSpaceCharsOnBothEnds = /^ /.test(text) && / $/.test(text);
if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {
text = text.substring(1, text.length - 1);
}
return {
type: 'codespan',
raw: cap[0],
text
};
}
return undefined;
},
lheading() {
return undefined;
},
url() {
return undefined;
},
// inlineText is overwritten to prevent html escaping, specifically since quote characters are escaped, which breaks the attributes of bb-code elements.
// The function is copied from marked.js and slightly modified: https://github.com/markedjs/marked/blob/42954aaba960b6f815b24ec0d39da464960e4ec9/src/Tokenizer.ts#L854
inlineText(src) {
const cap = inlineTextRule.exec(src);
if (cap) {
return {
type: 'text',
raw: cap[0],
text: cap[0]
};
}
return undefined;
}
};
const renderer = {
// Code Renderer is overwritten to prevent html escaping, since html is already escaped.
// The function is copied from marked.js and slightly modified: https://github.com/markedjs/marked/blob/42954aaba960b6f815b24ec0d39da464960e4ec9/src/Renderer.ts#L24
code(text, lang) {
const langString = (lang || '').match(/^\S*/)?.[0];
const code = `${text.replace(/\n$/, '')}`;
if (!langString) {
return `<pre><code>${code}</code></pre>\n`;
}
return `<pre><code class="language-${langString}">${code}</code></pre>\n`;
},
// Replaces the checkbox input elements with markdown checkboxes.
// This is the easiest way to prevent the formatting of markdown checkboxes in lists.
// This can modify the input string slightly, since the capitalization of the checkbox can be lost.
// If a user types '- [X]' it will be replaced with '- [x]' => the capitalization is lost.
checkbox(_ref) {
let {
checked
} = _ref;
return checked ? '[x]' : '[ ]';
}
};
const postprocess = html => {
let tableIndex = 0;
// Assigns ids to tables.
const modifiedString = html.replace(/(<table>)/g, () => {
const result = `<table id="${TABLE_ID_PREFIX}${tableIndex}">`;
tableIndex++;
return result;
});
return modifiedString;
};
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import InternalHTMLRenderer from './InternalHTMLRenderer';
export const parseMarkdown = text => {
let newText = text;
marked.use({
tokenizer,
renderer,
hooks: {
postprocess
}
});
// Markdown has its own line break handling. For that reason, we need to replace line breaks with a custom element.
// In this case I chose a custom <br> Tag.
// Since commonmark doesn't parse markdown in lines with html, the custom <br> Tag needs to be in its own line.
// Since there are issues, when the <br> Tag + \n is followed by text, there needs to be a second line break.
// Thus, we replace \n with \n<br>\n\n.
newText = newText.replaceAll(/\n/gm, `\n${MESSAGE_CONVERSION_LINE_BREAK}\n\n`);
const commonmarkParser = new Parser();
// TODO Check if esc needs to be passed to InternalHTMLRenderer.
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call
// @ts-ignore
const internalHTMLRenderer = new InternalHTMLRenderer({
esc: escapeHtmlInText
// Parses markdown following the Github Flavored Markdown specification.
// The tokenizer and renderer are slightly modified to prevent html escaping in code block and inline code.
export const parseMarkdown = (text, parseBBCode) => marked.parse(text, {
walkTokens: token => {
if (parseBBCode && (token.type === 'codespan' || token.type === 'code')) {
// eslint-disable-next-line no-param-reassign
token.text = escapeBBCodeSquareBrackets(token.text);
}
}
});
// It is important that, &amp; is replaced lastly to prevent double escaping.
const unescapeHtml = text => text.replaceAll('&lt;', '<').replaceAll('&gt;', '>').replaceAll('&amp;', '&');
export const getMarkdownTables = text => {
const tableTokens = [];
marked.parse(text, {
walkTokens: token => {
if (token.type === 'table') {
tableTokens.push(token);
}
}
});
// Converts markdown to HTML.
const parsedText = commonmarkParser.parse(newText);
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
newText = internalHTMLRenderer.render(parsedText);
// The Linebreak handling of markdown is ignored, by removing \n. Then the custom <br> Tags are converted back to \n.
newText = newText.replaceAll(/\n/gm, '');
newText = newText.replaceAll(MESSAGE_CONVERSION_LINE_BREAK, '\n').replaceAll(MESSAGE_CONVERSION_LINE_BREAK_ESCAPED, '\n');
return newText;
const tables = [];
tableTokens.forEach((tableToken, index) => {
const tableArray = [];
if (tableToken.header?.length > 0) {
const rowArray = [];
tableToken.header.forEach(header => {
rowArray.push(unescapeHtml(header.text));
});
tableArray.push(rowArray);
}
if (tableToken.rows?.length > 0) {
tableToken.rows.forEach(row => {
const rowArray = [];
row.forEach(cell => {
rowArray.push(unescapeHtml(cell.text));
});
tableArray.push(rowArray);
});
}
const csv = stringify(tableArray || []);
tables.push({
raw: unescapeHtml(tableToken.raw),
csv,
id: `${TABLE_ID_PREFIX}${index}`
});
});
return tables;
};
//# sourceMappingURL=formatMarkdown.js.map
export declare const escapeHtmlInText: (text: string) => string;
export declare const unescapeSquareBrackets: (text: string) => string;
export declare const MESSAGE_CONVERSION_LINE_BREAK = "<br is-replaced-linebreak>";
export declare const MESSAGE_CONVERSION_LINE_BREAK_ESCAPED: string;

@@ -7,3 +7,5 @@ export interface BBCodeMatch {

index: number;
openingTag: string;
closingTag: string;
}
export declare function findFirstBBCode(inputString: string): BBCodeMatch | null;
export interface ParseBBCodesOptions {
customBlockLevelBBCodeTags?: string[];
customInlineLevelBBCodeTags?: string[];
}
interface PrivateParseBBCodesOptions extends ParseBBCodesOptions {
justEscapeSquareBrackets?: boolean;
}
export declare const parseBBCode: (text: string, options?: PrivateParseBBCodesOptions) => string;
export {};
export declare const parseBBCode: (text: string, options?: ParseBBCodesOptions) => string;
export declare const escapeBBCodeSquareBrackets: (text: string) => string;
export declare const unescapeBBCodeSquareBrackets: (text: string) => string;

@@ -0,7 +1,5 @@

import type { TableObject } from '../../types/format';
import { ParseBBCodesOptions } from './bb-code/formatBBCode';
import { TableObject } from './markdown/formatMarkdownTable';
interface FormatStringOptions extends ParseBBCodesOptions {
escapeHtml?: boolean;
parseMarkdown?: boolean;
parseMarkdownTables?: boolean;
parseBBCode?: boolean;

@@ -8,0 +6,0 @@ }

@@ -1,1 +0,3 @@

export declare const parseMarkdown: (text: string) => string;
import type { TableObject } from '../../../types/format';
export declare const parseMarkdown: (text: string, parseBBCode: boolean) => string;
export declare const getMarkdownTables: (text: string) => TableObject[];
{
"name": "@chayns-components/format",
"version": "5.0.0-beta.651",
"version": "5.0.0-beta.674",
"description": "A set of beautiful React components for developing your own applications with chayns.",

@@ -46,3 +46,4 @@ "sideEffects": false,

"build:esm": "cross-env NODE_ENV=esm babel src --out-dir lib/esm --extensions=.ts,.tsx --source-maps --ignore=src/stories",
"prepublishOnly": "npm run build"
"prepublishOnly": "npm run build",
"test": "vitest"
},

@@ -63,3 +64,4 @@ "bugs": {

"lerna": "^8.1.3",
"typescript": "^5.4.5"
"typescript": "^5.4.5",
"vitest": "^1.6.0"
},

@@ -74,3 +76,3 @@ "dependencies": {

},
"gitHead": "4ed5eefd210a5a9caded6f2f9a08a24b3428251a"
"gitHead": "0a809ee9a5e3883ddeb7e1d3ab6c8805e0665693"
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc