expensify-common 2.0.95 → 2.0.97

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.
@@ -2,9 +2,26 @@ import Logger from './Logger';
2
2
  type Extras = {
3
3
  reportIDToName?: Record<string, string>;
4
4
  accountIDToName?: Record<string, string>;
5
+ /**
6
+ * @deprecated Replaced with mediaAttributeCachingFn
7
+ */
5
8
  cacheVideoAttributes?: (vidSource: string, attrs: string) => void;
9
+ /**
10
+ * @deprecated Replaced with mediaAttributeCache
11
+ */
6
12
  videoAttributeCache?: Record<string, string>;
13
+ /**
14
+ * Function used to cache HTML tag attributes during conversion to and from Markdown
15
+ * @param mediaSource URI to media source
16
+ * @param attrs Additional attributes to be cached
17
+ */
18
+ mediaAttributeCachingFn?: (mediaSource: string, attrs: string) => void;
19
+ /**
20
+ * Key/value cache for HTML tag attributes, where the key is media source URI, value is cached attributes
21
+ */
22
+ mediaAttributeCache?: Record<string, string>;
7
23
  };
24
+ export type { Extras };
8
25
  type ReplacementFn = (extras: Extras, ...matches: string[]) => string;
9
26
  type Replacement = ReplacementFn | string;
10
27
  type ProcessFn = (textToProcess: string, replacement: Replacement, shouldKeepRawInput: boolean) => string;
@@ -36,6 +53,10 @@ type TruncateOptions = {
36
53
  removeImageTag?: boolean;
37
54
  };
38
55
  export default class ExpensiMark {
56
+ getAttributeCache: (extras?: Extras) => {
57
+ attrCachingFn: ((vidSource: string, attrs: string) => void) | undefined;
58
+ attrCache: Record<string, string> | undefined;
59
+ };
39
60
  static Log: Logger;
40
61
  /**
41
62
  * Set the logger to use for logging inside of the ExpensiMark class
@@ -175,4 +196,3 @@ export default class ExpensiMark {
175
196
  */
176
197
  replaceTextWithExtras(text: string, regexp: RegExp, extras: Extras, replacement: Replacement): string;
177
198
  }
178
- export {};
@@ -46,6 +46,16 @@ class ExpensiMark {
46
46
  ExpensiMark.Log = logger;
47
47
  }
48
48
  constructor() {
49
+ this.getAttributeCache = (extras) => {
50
+ var _a, _b;
51
+ if (!extras) {
52
+ return { attrCachingFn: undefined, attrCache: undefined };
53
+ }
54
+ return {
55
+ attrCachingFn: (_a = extras.mediaAttributeCachingFn) !== null && _a !== void 0 ? _a : extras.cacheVideoAttributes,
56
+ attrCache: (_b = extras.mediaAttributeCache) !== null && _b !== void 0 ? _b : extras.videoAttributeCache,
57
+ };
58
+ };
49
59
  /**
50
60
  * The list of rules that we have to exclude in shouldKeepWhitespaceRules list.
51
61
  */
@@ -99,11 +109,13 @@ class ExpensiMark {
99
109
  * @return Returns the HTML video tag
100
110
  */
101
111
  replacement: (extras, _match, videoName, videoSource) => {
102
- const extraAttrs = extras && extras.videoAttributeCache && extras.videoAttributeCache[videoSource];
112
+ const attrCache = this.getAttributeCache(extras).attrCache;
113
+ const extraAttrs = attrCache && attrCache[videoSource];
103
114
  return `<video data-expensify-source="${str_1.default.sanitizeURL(videoSource)}" ${extraAttrs || ''}>${videoName ? `${videoName}` : ''}</video>`;
104
115
  },
105
116
  rawInputReplacement: (extras, _match, videoName, videoSource) => {
106
- const extraAttrs = extras && extras.videoAttributeCache && extras.videoAttributeCache[videoSource];
117
+ const attrCache = this.getAttributeCache(extras).attrCache;
118
+ const extraAttrs = attrCache && attrCache[videoSource];
107
119
  return `<video data-expensify-source="${str_1.default.sanitizeURL(videoSource)}" data-raw-href="${videoSource}" data-link-variant="${typeof videoName === 'string' ? 'labeled' : 'auto'}" ${extraAttrs || ''}>${videoName ? `${videoName}` : ''}</video>`;
108
120
  },
109
121
  },
@@ -167,12 +179,21 @@ class ExpensiMark {
167
179
  * tag.
168
180
  * Additional sanitization is done to the alt attribute to prevent parsing it further to html by later
169
181
  * rules.
182
+ * Additional tags from cache are applied to the result HTML.
170
183
  */
171
184
  {
172
185
  name: 'image',
173
186
  regex: MARKDOWN_IMAGE_REGEX,
174
- replacement: (_extras, _match, g1, g2) => `<img src="${str_1.default.sanitizeURL(g2)}"${g1 ? ` alt="${this.escapeAttributeContent(g1)}"` : ''} />`,
175
- rawInputReplacement: (_extras, _match, g1, g2) => `<img src="${str_1.default.sanitizeURL(g2)}"${g1 ? ` alt="${this.escapeAttributeContent(g1)}"` : ''} data-raw-href="${g2}" data-link-variant="${typeof g1 === 'string' ? 'labeled' : 'auto'}" />`,
187
+ replacement: (extras, _match, imgAlt, imgSource) => {
188
+ const attrCache = this.getAttributeCache(extras).attrCache;
189
+ const extraAttrs = attrCache && attrCache[imgSource];
190
+ return `<img src="${str_1.default.sanitizeURL(imgSource)}"${imgAlt ? ` alt="${this.escapeAttributeContent(imgAlt)}"` : ''} ${extraAttrs || ''}/>`;
191
+ },
192
+ rawInputReplacement: (extras, _match, imgAlt, imgSource) => {
193
+ const attrCache = this.getAttributeCache(extras).attrCache;
194
+ const extraAttrs = attrCache && attrCache[imgSource];
195
+ return `<img src="${str_1.default.sanitizeURL(imgSource)}"${imgAlt ? ` alt="${this.escapeAttributeContent(imgAlt)}"` : ''} data-raw-href="${imgSource}" data-link-variant="${typeof imgAlt === 'string' ? 'labeled' : 'auto'}" ${extraAttrs || ''}/>`;
196
+ },
176
197
  },
177
198
  /**
178
199
  * Converts markdown style links to anchor tags e.g. [Expensify](https://www.expensify.com)
@@ -543,12 +564,38 @@ class ExpensiMark {
543
564
  },
544
565
  {
545
566
  name: 'image',
546
- regex: /<img[^><]*src\s*=\s*(['"])(.*?)\1(?:[^><]*alt\s*=\s*(['"])(.*?)\3)?[^><]*>*(?![^<][\s\S]*?(<\/pre>|<\/code>))/gi,
547
- replacement: (_extras, _match, _g1, g2, _g3, g4) => {
548
- if (g4) {
549
- return `![${g4}](${g2})`;
567
+ regex: /<img[^><]*src\s*=\s*(['"])(.*?)\1(.*?)>(?![^<][\s\S]*?(<\/pre>|<\/code>))/gi,
568
+ /**
569
+ * @param extras - The extras object
570
+ * @param _match - The full match
571
+ * @param _g1 - The first capture group (the quote)
572
+ * @param imgSource - The second capture group - src attribute value
573
+ * @param imgAttrs - The third capture group - any attributes after src
574
+ * @returns The markdown image tag
575
+ */
576
+ replacement: (extras, _match, _g1, imgSource, imgAttrs) => {
577
+ // Extract alt attribute from imgAttrs
578
+ let altText = '';
579
+ const altRegex = /alt\s*=\s*(['"])(.*?)\1/i;
580
+ const altMatch = imgAttrs.match(altRegex);
581
+ const attrCachingFn = this.getAttributeCache(extras).attrCachingFn;
582
+ let attributes = imgAttrs;
583
+ if (altMatch) {
584
+ altText = altMatch[2];
585
+ // Remove the alt attribute from imgAttrs
586
+ attributes = attributes.replace(altRegex, '');
587
+ }
588
+ // Remove trailing slash and extra whitespace
589
+ attributes = attributes.replace(/\s*\/\s*$/, '').trim();
590
+ // Cache attributes without alt and trailing slash
591
+ if (imgAttrs && attrCachingFn && typeof attrCachingFn === 'function') {
592
+ attrCachingFn(imgSource, attributes);
593
+ }
594
+ // Return the markdown image tag
595
+ if (altText) {
596
+ return `![${altText}](${imgSource})`;
550
597
  }
551
- return `!(${g2})`;
598
+ return `!(${imgSource})`;
552
599
  },
553
600
  },
554
601
  {
@@ -564,8 +611,9 @@ class ExpensiMark {
564
611
  * @returns The markdown video tag
565
612
  */
566
613
  replacement: (extras, _match, _g1, videoSource, videoAttrs, videoName) => {
567
- if (videoAttrs && extras && extras.cacheVideoAttributes && typeof extras.cacheVideoAttributes === 'function') {
568
- extras.cacheVideoAttributes(videoSource, videoAttrs);
614
+ const attrCachingFn = this.getAttributeCache(extras).attrCachingFn;
615
+ if (videoAttrs && attrCachingFn && typeof attrCachingFn === 'function') {
616
+ attrCachingFn(videoSource, videoAttrs);
569
617
  }
570
618
  if (videoName) {
571
619
  return `![${videoName}](${videoSource})`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expensify-common",
3
- "version": "2.0.95",
3
+ "version": "2.0.97",
4
4
  "author": "Expensify, Inc.",
5
5
  "description": "Expensify libraries and components shared across different repos",
6
6
  "homepage": "https://expensify.com",
@@ -68,7 +68,7 @@
68
68
  "jest-environment-jsdom": "^29.7.0",
69
69
  "jit-grunt": "^0.10.0",
70
70
  "prettier": "^3.3.3",
71
- "typescript": "^5.5.4"
71
+ "typescript": "^5.6.2"
72
72
  },
73
73
  "browserify": {
74
74
  "transform": [