expensify-common 2.0.108 → 2.0.110
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/CONST.d.ts +4 -0
- package/dist/CONST.js +4 -0
- package/dist/ExpensiMark.d.ts +28 -6
- package/dist/ExpensiMark.js +90 -120
- package/package.json +1 -1
package/dist/CONST.d.ts
CHANGED
|
@@ -804,6 +804,10 @@ declare const CONST: {
|
|
|
804
804
|
readonly longFlightFare: "economy";
|
|
805
805
|
readonly hotelStar: "fourStars";
|
|
806
806
|
};
|
|
807
|
+
readonly PAYMENT_TYPE: {
|
|
808
|
+
readonly PAY_AT_HOTEL: "PAY_AT_HOTEL";
|
|
809
|
+
readonly PAY_AT_VENDOR: "PAY_AT_VENDOR";
|
|
810
|
+
};
|
|
807
811
|
};
|
|
808
812
|
readonly EXPENSIFY_DOMAINS: readonly ["expensify.com", "expensifail.com", "expensicorp.com"];
|
|
809
813
|
readonly SUBSCRIPTION_CHANGE_REASONS: {
|
package/dist/CONST.js
CHANGED
|
@@ -845,6 +845,10 @@ const CONST = {
|
|
|
845
845
|
longFlightFare: 'economy',
|
|
846
846
|
hotelStar: 'fourStars',
|
|
847
847
|
},
|
|
848
|
+
PAYMENT_TYPE: {
|
|
849
|
+
PAY_AT_HOTEL: 'PAY_AT_HOTEL',
|
|
850
|
+
PAY_AT_VENDOR: 'PAY_AT_VENDOR',
|
|
851
|
+
},
|
|
848
852
|
},
|
|
849
853
|
// Expensify domains
|
|
850
854
|
EXPENSIFY_DOMAINS: ['expensify.com', 'expensifail.com', 'expensicorp.com'],
|
package/dist/ExpensiMark.d.ts
CHANGED
|
@@ -131,6 +131,27 @@ export default class ExpensiMark {
|
|
|
131
131
|
* 4. It's not the last element in the string.
|
|
132
132
|
*/
|
|
133
133
|
replaceBlockElementWithNewLine(htmlString: string): string;
|
|
134
|
+
/**
|
|
135
|
+
* Unpacks nested quote HTML tags that have been packed by the 'quote' rule in this.rules for shouldKeepRawInput = false
|
|
136
|
+
*
|
|
137
|
+
* For example, it parses the following HTML:
|
|
138
|
+
* <blockquote>
|
|
139
|
+
* quote 1
|
|
140
|
+
* <blockquote>
|
|
141
|
+
* quote 2
|
|
142
|
+
* </blockquote>
|
|
143
|
+
* quote 3
|
|
144
|
+
* </blockquote>
|
|
145
|
+
*
|
|
146
|
+
* into:
|
|
147
|
+
* <blockquote> quote 1</blockquote>
|
|
148
|
+
* <blockquote><blockquote> quote 2</blockquote>
|
|
149
|
+
* <blockquote> quote 3</blockquote>
|
|
150
|
+
*
|
|
151
|
+
* Note that there will always be only a single closing tag, even if multiple opening tags exist.
|
|
152
|
+
* Only one closing tag is needed to detect if a nested quote has ended.
|
|
153
|
+
*/
|
|
154
|
+
unpackNestedQuotes(text: string): string;
|
|
134
155
|
/**
|
|
135
156
|
* Replaces HTML with markdown
|
|
136
157
|
*/
|
|
@@ -140,13 +161,14 @@ export default class ExpensiMark {
|
|
|
140
161
|
*/
|
|
141
162
|
htmlToText(htmlString: string, extras?: Extras): string;
|
|
142
163
|
/**
|
|
143
|
-
*
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Format the content of blockquote if the text matches the regex or else just return the original text
|
|
164
|
+
* Main text to html 'quote' parsing logic.
|
|
165
|
+
* Removes >( ) from text and recursively calls replace function to process nested quotes and build blockquote HTML result.
|
|
166
|
+
* @param shouldKeepRawInput determines if the raw input should be kept for nested quotes.
|
|
148
167
|
*/
|
|
149
|
-
|
|
168
|
+
replaceQuoteText(text: string, shouldKeepRawInput: boolean): {
|
|
169
|
+
replacedText: string;
|
|
170
|
+
shouldAddSpace: boolean;
|
|
171
|
+
};
|
|
150
172
|
/**
|
|
151
173
|
* Check if the input text includes only the open or the close tag of an element.
|
|
152
174
|
*/
|
package/dist/ExpensiMark.js
CHANGED
|
@@ -306,54 +306,24 @@ class ExpensiMark {
|
|
|
306
306
|
// block quotes naturally appear on their own line. Blockquotes should not appear in code fences or
|
|
307
307
|
// inline code blocks. A single prepending space should be stripped if it exists
|
|
308
308
|
process: (textToProcess, replacement, shouldKeepRawInput = false) => {
|
|
309
|
-
const regex = /^(?:>)+ +(?! )(?![^<]*(?:<\/pre>|<\/code>|<\/video>))([^\v\n\r]
|
|
309
|
+
const regex = /^(?:>)+ +(?! )(?![^<]*(?:<\/pre>|<\/code>|<\/video>))([^\v\n\r]*)/gm;
|
|
310
|
+
let replacedText = this.replaceTextWithExtras(textToProcess, regex, EXTRAS_DEFAULT, replacement);
|
|
310
311
|
if (shouldKeepRawInput) {
|
|
311
|
-
|
|
312
|
-
return this.replaceTextWithExtras(textToProcess, rawInputRegex, EXTRAS_DEFAULT, replacement);
|
|
312
|
+
return replacedText;
|
|
313
313
|
}
|
|
314
|
-
|
|
314
|
+
for (let i = this.maxQuoteDepth; i > 0; i--) {
|
|
315
|
+
replacedText = replacedText.replaceAll(`${'</blockquote>'.repeat(i)}\n${'<blockquote>'.repeat(i)}`, '\n');
|
|
316
|
+
}
|
|
317
|
+
replacedText = replacedText.replaceAll('</blockquote>\n', '</blockquote>');
|
|
318
|
+
return replacedText;
|
|
315
319
|
},
|
|
316
320
|
replacement: (_extras, g1) => {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
const handleMatch = (match) => match;
|
|
320
|
-
const textToReplace = g1.replace(/^>( )?/gm, handleMatch);
|
|
321
|
-
const filterRules = ['heading1'];
|
|
322
|
-
// if we don't reach the max quote depth we allow the recursive call to process possible quote
|
|
323
|
-
if (this.currentQuoteDepth < this.maxQuoteDepth - 1) {
|
|
324
|
-
filterRules.push('quote');
|
|
325
|
-
this.currentQuoteDepth++;
|
|
326
|
-
}
|
|
327
|
-
const replacedText = this.replace(textToReplace, {
|
|
328
|
-
filterRules,
|
|
329
|
-
shouldEscapeText: false,
|
|
330
|
-
shouldKeepRawInput: false,
|
|
331
|
-
});
|
|
332
|
-
this.currentQuoteDepth = 0;
|
|
333
|
-
return `<blockquote>${replacedText}</blockquote>`;
|
|
321
|
+
const { replacedText } = this.replaceQuoteText(g1, false);
|
|
322
|
+
return `<blockquote>${replacedText || ' '}</blockquote>`;
|
|
334
323
|
},
|
|
335
324
|
rawInputReplacement: (_extras, g1) => {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
let isStartingWithSpace = false;
|
|
339
|
-
const handleMatch = (_match, g2) => {
|
|
340
|
-
isStartingWithSpace = !!g2;
|
|
341
|
-
return '';
|
|
342
|
-
};
|
|
343
|
-
const textToReplace = g1.replace(/^>( )?/gm, handleMatch);
|
|
344
|
-
const filterRules = ['heading1'];
|
|
345
|
-
// if we don't reach the max quote depth we allow the recursive call to process possible quote
|
|
346
|
-
if (this.currentQuoteDepth < this.maxQuoteDepth - 1 || isStartingWithSpace) {
|
|
347
|
-
filterRules.push('quote');
|
|
348
|
-
this.currentQuoteDepth++;
|
|
349
|
-
}
|
|
350
|
-
const replacedText = this.replace(textToReplace, {
|
|
351
|
-
filterRules,
|
|
352
|
-
shouldEscapeText: false,
|
|
353
|
-
shouldKeepRawInput: true,
|
|
354
|
-
});
|
|
355
|
-
this.currentQuoteDepth = 0;
|
|
356
|
-
return `<blockquote>${isStartingWithSpace ? ' ' : ''}${replacedText}</blockquote>`;
|
|
325
|
+
const { replacedText, shouldAddSpace } = this.replaceQuoteText(g1, true);
|
|
326
|
+
return `<blockquote>${shouldAddSpace ? ' ' : ''}${replacedText}</blockquote>`;
|
|
357
327
|
},
|
|
358
328
|
},
|
|
359
329
|
/**
|
|
@@ -957,8 +927,9 @@ class ExpensiMark {
|
|
|
957
927
|
if (text.trim().length === 0 && !text.match(/\n/)) {
|
|
958
928
|
return;
|
|
959
929
|
}
|
|
930
|
+
const nextItem = splitText === null || splitText === void 0 ? void 0 : splitText[index + 1];
|
|
960
931
|
// Insert '\n' unless it ends with '\n' or '>' or it's the last element, or if it's a header ('# ') with a space.
|
|
961
|
-
if (text.match(
|
|
932
|
+
if ((nextItem && text.match(/>[\s]?$/) && !nextItem.startsWith('> ')) || text.match(/\n[\s]?$/) || index === splitText.length - 1 || text === '# ') {
|
|
962
933
|
joinedText += text;
|
|
963
934
|
}
|
|
964
935
|
else {
|
|
@@ -968,6 +939,59 @@ class ExpensiMark {
|
|
|
968
939
|
splitText.forEach(processText);
|
|
969
940
|
return joinedText;
|
|
970
941
|
}
|
|
942
|
+
/**
|
|
943
|
+
* Unpacks nested quote HTML tags that have been packed by the 'quote' rule in this.rules for shouldKeepRawInput = false
|
|
944
|
+
*
|
|
945
|
+
* For example, it parses the following HTML:
|
|
946
|
+
* <blockquote>
|
|
947
|
+
* quote 1
|
|
948
|
+
* <blockquote>
|
|
949
|
+
* quote 2
|
|
950
|
+
* </blockquote>
|
|
951
|
+
* quote 3
|
|
952
|
+
* </blockquote>
|
|
953
|
+
*
|
|
954
|
+
* into:
|
|
955
|
+
* <blockquote> quote 1</blockquote>
|
|
956
|
+
* <blockquote><blockquote> quote 2</blockquote>
|
|
957
|
+
* <blockquote> quote 3</blockquote>
|
|
958
|
+
*
|
|
959
|
+
* Note that there will always be only a single closing tag, even if multiple opening tags exist.
|
|
960
|
+
* Only one closing tag is needed to detect if a nested quote has ended.
|
|
961
|
+
*/
|
|
962
|
+
unpackNestedQuotes(text) {
|
|
963
|
+
let parsedText = text.replace(/((<\/blockquote>)+(<br \/>)?)|(<br \/>)/g, (match) => {
|
|
964
|
+
return `${match}</split>`;
|
|
965
|
+
});
|
|
966
|
+
const splittedText = parsedText.split('</split>');
|
|
967
|
+
if (splittedText.length > 0 && splittedText[splittedText.length - 1] === '') {
|
|
968
|
+
splittedText.pop();
|
|
969
|
+
}
|
|
970
|
+
let count = 0;
|
|
971
|
+
parsedText = splittedText
|
|
972
|
+
.map((line) => {
|
|
973
|
+
const hasBR = line.endsWith('<br />');
|
|
974
|
+
if (line === '' && count === 0) {
|
|
975
|
+
return '';
|
|
976
|
+
}
|
|
977
|
+
const textLine = line.replace(/(<br \/>)$/g, '');
|
|
978
|
+
if (textLine.startsWith('<blockquote>')) {
|
|
979
|
+
count += (textLine.match(/<blockquote>/g) || []).length;
|
|
980
|
+
}
|
|
981
|
+
if (textLine.endsWith('</blockquote>')) {
|
|
982
|
+
count -= (textLine.match(/<\/blockquote>/g) || []).length;
|
|
983
|
+
if (count > 0) {
|
|
984
|
+
return `${textLine}${'<blockquote>'.repeat(count)}`;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
if (count > 0) {
|
|
988
|
+
return `${textLine}${'</blockquote>'}${'<blockquote>'.repeat(count)}`;
|
|
989
|
+
}
|
|
990
|
+
return textLine + (hasBR ? '<br />' : '');
|
|
991
|
+
})
|
|
992
|
+
.join('');
|
|
993
|
+
return parsedText;
|
|
994
|
+
}
|
|
971
995
|
/**
|
|
972
996
|
* Replaces HTML with markdown
|
|
973
997
|
*/
|
|
@@ -979,6 +1003,7 @@ class ExpensiMark {
|
|
|
979
1003
|
if (parseBodyTag) {
|
|
980
1004
|
generatedMarkdown = parseBodyTag[2];
|
|
981
1005
|
}
|
|
1006
|
+
generatedMarkdown = this.unpackNestedQuotes(generatedMarkdown);
|
|
982
1007
|
const processRule = (rule) => {
|
|
983
1008
|
// Pre-processes input HTML before applying regex
|
|
984
1009
|
if (rule.pre) {
|
|
@@ -1004,85 +1029,30 @@ class ExpensiMark {
|
|
|
1004
1029
|
return replacedText;
|
|
1005
1030
|
}
|
|
1006
1031
|
/**
|
|
1007
|
-
*
|
|
1032
|
+
* Main text to html 'quote' parsing logic.
|
|
1033
|
+
* Removes >( ) from text and recursively calls replace function to process nested quotes and build blockquote HTML result.
|
|
1034
|
+
* @param shouldKeepRawInput determines if the raw input should be kept for nested quotes.
|
|
1008
1035
|
*/
|
|
1009
|
-
|
|
1010
|
-
let
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
insideCodefence = str_1.default.contains(textSplitted[i], '<pre>');
|
|
1022
|
-
}
|
|
1023
|
-
// Since the last space will be trimmed and would incorrectly disable a condition we check it manually
|
|
1024
|
-
const isLastBlockquote = textSplitted[i] === '>' && i === textSplitted.length - 1;
|
|
1025
|
-
// We only want to modify lines starting with "> " that is not codefence
|
|
1026
|
-
if ((str_1.default.startsWith(textSplitted[i], '> ') || isLastBlockquote) && !insideCodefence) {
|
|
1027
|
-
if (textSplitted[i] === '>') {
|
|
1028
|
-
textToFormat += `${textSplitted[i]} \n`;
|
|
1029
|
-
insideCodefence = true;
|
|
1030
|
-
}
|
|
1031
|
-
else {
|
|
1032
|
-
textToFormat += `${textSplitted[i]}\n`;
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
else {
|
|
1036
|
-
// Make sure we will only modify if we have Text needed to be formatted for quote
|
|
1037
|
-
if (textToFormat !== '') {
|
|
1038
|
-
replacedText += this.formatTextForQuote(regex, textToFormat, replacement);
|
|
1039
|
-
textToFormat = '';
|
|
1040
|
-
}
|
|
1041
|
-
// We dont want a \n after the textSplitted if it is the last row
|
|
1042
|
-
if (i === textSplitted.length - 1) {
|
|
1043
|
-
replacedText += `${textSplitted[i]}`;
|
|
1044
|
-
}
|
|
1045
|
-
else {
|
|
1046
|
-
replacedText += `${textSplitted[i]}\n`;
|
|
1047
|
-
}
|
|
1048
|
-
// We need to know when we are not inside codefence anymore
|
|
1049
|
-
if (insideCodefence) {
|
|
1050
|
-
insideCodefence = !str_1.default.contains(textSplitted[i], '</pre>');
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
// When loop ends we need the last quote to be formatted if we have quotes at last rows
|
|
1055
|
-
if (textToFormat !== '') {
|
|
1056
|
-
replacedText += this.formatTextForQuote(regex, textToFormat, replacement);
|
|
1057
|
-
}
|
|
1058
|
-
}
|
|
1059
|
-
else {
|
|
1060
|
-
// If we doesn't have matches make sure the function will return the same textToCheck
|
|
1061
|
-
replacedText = textToCheck;
|
|
1062
|
-
}
|
|
1063
|
-
return replacedText;
|
|
1064
|
-
}
|
|
1065
|
-
/**
|
|
1066
|
-
* Format the content of blockquote if the text matches the regex or else just return the original text
|
|
1067
|
-
*/
|
|
1068
|
-
formatTextForQuote(regex, textToCheck, replacement) {
|
|
1069
|
-
if (textToCheck.match(regex)) {
|
|
1070
|
-
// Remove '>' and trim the spaces between nested quotes
|
|
1071
|
-
const formatRow = (row) => {
|
|
1072
|
-
let quoteContent = row[4] === ' ' ? row.substr(5) : row.substr(4);
|
|
1073
|
-
if (row === '> ')
|
|
1074
|
-
quoteContent = row.substr(4);
|
|
1075
|
-
if (quoteContent.trimStart().startsWith('>')) {
|
|
1076
|
-
return quoteContent.trimStart();
|
|
1077
|
-
}
|
|
1078
|
-
return quoteContent;
|
|
1079
|
-
};
|
|
1080
|
-
let textToFormat = textToCheck.split('\n').map(formatRow).join('\n');
|
|
1081
|
-
// Remove leading and trailing line breaks
|
|
1082
|
-
textToFormat = textToFormat.replace(/^\n+|\n+$/g, '');
|
|
1083
|
-
return replacement(EXTRAS_DEFAULT, textToFormat);
|
|
1036
|
+
replaceQuoteText(text, shouldKeepRawInput) {
|
|
1037
|
+
let isStartingWithSpace = false;
|
|
1038
|
+
const handleMatch = (_match, g2) => {
|
|
1039
|
+
isStartingWithSpace = !!g2;
|
|
1040
|
+
return '';
|
|
1041
|
+
};
|
|
1042
|
+
const textToReplace = text.replace(/^>( )?/gm, handleMatch);
|
|
1043
|
+
const filterRules = ['heading1'];
|
|
1044
|
+
// If we don't reach the max quote depth, we allow the recursive call to process other possible quotes
|
|
1045
|
+
if (this.currentQuoteDepth < this.maxQuoteDepth - 1 && !isStartingWithSpace) {
|
|
1046
|
+
filterRules.push('quote');
|
|
1047
|
+
this.currentQuoteDepth++;
|
|
1084
1048
|
}
|
|
1085
|
-
|
|
1049
|
+
const replacedText = this.replace(textToReplace, {
|
|
1050
|
+
filterRules,
|
|
1051
|
+
shouldEscapeText: false,
|
|
1052
|
+
shouldKeepRawInput,
|
|
1053
|
+
});
|
|
1054
|
+
this.currentQuoteDepth = 0;
|
|
1055
|
+
return { replacedText, shouldAddSpace: isStartingWithSpace };
|
|
1086
1056
|
}
|
|
1087
1057
|
/**
|
|
1088
1058
|
* Check if the input text includes only the open or the close tag of an element.
|