expensify-common 2.0.180 → 2.0.181
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/ExpensiMark.d.ts +5 -0
- package/dist/ExpensiMark.js +37 -3
- package/package.json +1 -1
package/dist/ExpensiMark.d.ts
CHANGED
|
@@ -54,6 +54,11 @@ type TruncateOptions = {
|
|
|
54
54
|
removeImageTag?: boolean;
|
|
55
55
|
};
|
|
56
56
|
export default class ExpensiMark {
|
|
57
|
+
extractVictoryChartTags: (text: string) => {
|
|
58
|
+
text: string;
|
|
59
|
+
tags: string[];
|
|
60
|
+
};
|
|
61
|
+
restoreVictoryChartTags: (text: string, tags: string[]) => string;
|
|
57
62
|
getAttributeCache: (extras?: Extras) => {
|
|
58
63
|
attrCachingFn: ((vidSource: string, attrs: string) => void) | undefined;
|
|
59
64
|
attrCache: Record<string, string> | undefined;
|
package/dist/ExpensiMark.js
CHANGED
|
@@ -47,6 +47,13 @@ const MARKDOWN_LINK_REGEX = new RegExp(`\\[((?:[^\\[\\]\\r\\n]*(?:\\[[^\\[\\]\\r
|
|
|
47
47
|
const MARKDOWN_IMAGE_REGEX = new RegExp(`\\!(?:\\[([^\\][]*(?:\\[[^\\][]*][^\\][]*)*)])?\\(${UrlPatterns.MARKDOWN_URL_REGEX}\\)(?![^<]*(<\\/pre>|<\\/code>))`, 'gi');
|
|
48
48
|
const MARKDOWN_VIDEO_REGEX = new RegExp(`\\!(?:\\[([^\\][]*(?:\\[[^\\][]*][^\\][]*)*)])?\\(((${UrlPatterns.MARKDOWN_URL_REGEX})\\.(?:${Constants.CONST.VIDEO_EXTENSIONS.join('|')}))\\)(?![^<]*(<\\/pre>|<\\/code>))`, 'gi');
|
|
49
49
|
const SLACK_SPAN_NEW_LINE_TAG = '<span class="c-mrkdwn__br" data-stringify-type="paragraph-break" style="box-sizing: inherit; display: block; height: unset;"></span>';
|
|
50
|
+
// Preserve VirtualCFO chart blocks by matching the outer <VictoryChart> container.
|
|
51
|
+
// This captures all nested Victory components and prevents markup escaping during conversion.
|
|
52
|
+
const VICTORY_CHART_REGEX = /<VictoryChart\b[^>]*\/>|<VictoryChart\b[^>]*>[\s\S]*?<\/VictoryChart>/gi;
|
|
53
|
+
// Chart placeholders use NUL characters as delimiters to prevent conflicts with user content.
|
|
54
|
+
const VICTORY_CHART_PLACEHOLDER_DELIMITER = String.fromCharCode(0);
|
|
55
|
+
const VICTORY_CHART_PLACEHOLDER_PATTERN = new RegExp(`${VICTORY_CHART_PLACEHOLDER_DELIMITER}(\\d+)${VICTORY_CHART_PLACEHOLDER_DELIMITER}`, 'g');
|
|
56
|
+
const createVictoryChartPlaceholder = (index) => `${VICTORY_CHART_PLACEHOLDER_DELIMITER}${index}${VICTORY_CHART_PLACEHOLDER_DELIMITER}`;
|
|
50
57
|
class ExpensiMark {
|
|
51
58
|
/**
|
|
52
59
|
* Set the logger to use for logging inside of the ExpensiMark class
|
|
@@ -56,6 +63,21 @@ class ExpensiMark {
|
|
|
56
63
|
ExpensiMark.Log = logger;
|
|
57
64
|
}
|
|
58
65
|
constructor() {
|
|
66
|
+
this.extractVictoryChartTags = (text) => {
|
|
67
|
+
const tags = [];
|
|
68
|
+
const out = text.replace(VICTORY_CHART_REGEX, (match) => {
|
|
69
|
+
const placeholder = createVictoryChartPlaceholder(tags.length);
|
|
70
|
+
tags.push(match);
|
|
71
|
+
return placeholder;
|
|
72
|
+
});
|
|
73
|
+
return { text: out, tags };
|
|
74
|
+
};
|
|
75
|
+
this.restoreVictoryChartTags = (text, tags) => {
|
|
76
|
+
if (tags.length === 0) {
|
|
77
|
+
return text;
|
|
78
|
+
}
|
|
79
|
+
return text.replace(VICTORY_CHART_PLACEHOLDER_PATTERN, (match, idx) => { var _a; return (_a = tags[Number(idx)]) !== null && _a !== void 0 ? _a : match; });
|
|
80
|
+
};
|
|
59
81
|
this.getAttributeCache = (extras) => {
|
|
60
82
|
var _a, _b;
|
|
61
83
|
if (!extras) {
|
|
@@ -725,6 +747,11 @@ class ExpensiMark {
|
|
|
725
747
|
regex: /<video[^><]*data-expensify-source\s*=\s*(['"])(\S*?)\1(.*?)>([^><]*)<\/video>*(?![^<][\s\S]*?(<\/pre>|<\/code>))/gi,
|
|
726
748
|
replacement: '[Attachment]',
|
|
727
749
|
},
|
|
750
|
+
{
|
|
751
|
+
name: 'victoryChart',
|
|
752
|
+
regex: /<VictoryChart\b[^>]*\/>|<VictoryChart\b[^>]*>[\s\S]*?<\/VictoryChart>/gi,
|
|
753
|
+
replacement: '[chart]',
|
|
754
|
+
},
|
|
728
755
|
{
|
|
729
756
|
name: 'otherAttachments',
|
|
730
757
|
regex: /<a[^><]*data-expensify-source\s*=\s*(['"])(\S*?)\1(.*?)>([^><]*)<\/a>*(?![^<][\s\S]*?(<\/pre>|<\/code>))/gi,
|
|
@@ -824,8 +851,11 @@ class ExpensiMark {
|
|
|
824
851
|
if (!text) {
|
|
825
852
|
return '';
|
|
826
853
|
}
|
|
854
|
+
// Extract VictoryChart blocks to preserve their markup during processing.
|
|
855
|
+
// Only safe for trusted server input - user input must be escaped to prevent XSS.
|
|
856
|
+
const { text: textWithPlaceholders, tags: victoryChartTags } = shouldEscapeText ? { text, tags: [] } : this.extractVictoryChartTags(text);
|
|
827
857
|
// This ensures that any html the user puts into the comment field shows as raw html
|
|
828
|
-
let replacedText = shouldEscapeText ? Utils.escapeText(
|
|
858
|
+
let replacedText = shouldEscapeText ? Utils.escapeText(textWithPlaceholders) : textWithPlaceholders;
|
|
829
859
|
const rules = this.getHtmlRuleset(filterRules, disabledRules, shouldKeepRawInput);
|
|
830
860
|
const processRule = (rule) => {
|
|
831
861
|
// Pre-process text before applying regex
|
|
@@ -855,7 +885,7 @@ class ExpensiMark {
|
|
|
855
885
|
// We want to return text without applying rules if exception occurs during replacing
|
|
856
886
|
return shouldEscapeText ? Utils.escapeText(text) : text;
|
|
857
887
|
}
|
|
858
|
-
return replacedText;
|
|
888
|
+
return this.restoreVictoryChartTags(replacedText, victoryChartTags);
|
|
859
889
|
}
|
|
860
890
|
/**
|
|
861
891
|
* Checks matched URLs for validity and replace valid links with html elements
|
|
@@ -1087,6 +1117,9 @@ class ExpensiMark {
|
|
|
1087
1117
|
generatedMarkdown = parseBodyTag[2];
|
|
1088
1118
|
}
|
|
1089
1119
|
generatedMarkdown = this.unpackNestedQuotes(generatedMarkdown);
|
|
1120
|
+
// Extract VictoryChart blocks before HTML stripping, then restore them.
|
|
1121
|
+
const { text: textWithPlaceholders, tags: victoryChartTags } = this.extractVictoryChartTags(generatedMarkdown);
|
|
1122
|
+
generatedMarkdown = textWithPlaceholders;
|
|
1090
1123
|
const processRule = (rule) => {
|
|
1091
1124
|
// Pre-processes input HTML before applying regex
|
|
1092
1125
|
if (rule.pre) {
|
|
@@ -1095,7 +1128,8 @@ class ExpensiMark {
|
|
|
1095
1128
|
generatedMarkdown = this.replaceTextWithExtras(generatedMarkdown, rule.regex, extras, rule.replacement);
|
|
1096
1129
|
};
|
|
1097
1130
|
this.htmlToMarkdownRules.forEach(processRule);
|
|
1098
|
-
|
|
1131
|
+
const decoded = str_1.default.htmlDecode(this.replaceBlockElementWithNewLine(generatedMarkdown));
|
|
1132
|
+
return this.restoreVictoryChartTags(decoded, victoryChartTags);
|
|
1099
1133
|
}
|
|
1100
1134
|
/**
|
|
1101
1135
|
* Convert HTML to text
|