@expensify/react-native-live-markdown 0.1.326 → 0.1.328
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/README.md
CHANGED
|
@@ -193,27 +193,27 @@ Currently, `react-native-live-markdown` supports only [ExpensiMark](https://gith
|
|
|
193
193
|
|
|
194
194
|
### React Native compatibility
|
|
195
195
|
|
|
196
|
-
| | 0.73 | 0.74 | 0.75 | 0.76 | 0.77 | 0.78 | 0.79 | 0.80 | 0.81 | 0.82 | 0.83 |
|
|
197
|
-
|
|
198
|
-
| 0.1.321+ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
199
|
-
| 0.1.304 - 0.1.320 | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
|
200
|
-
| 0.1.297 – 0.1.303 | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
|
|
201
|
-
| 0.1.260 – 0.1.296 | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
202
|
-
| 0.1.256 – 0.1.259 | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
203
|
-
| 0.1.248 – 0.1.255 | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
204
|
-
| 0.1.235 – 0.1.247 | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
205
|
-
| 0.1.141 – 0.1.234 | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
206
|
-
| 0.1.129 – 0.1.140 | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
207
|
-
| 0.1.122 – 0.1.128 | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
208
|
-
| 0.1.15 – 0.1.121 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
196
|
+
| | 0.73 | 0.74 | 0.75 | 0.76 | 0.77 | 0.78 | 0.79 | 0.80 | 0.81 | 0.82 | 0.83 | 0.84 | 0.85 |
|
|
197
|
+
|:-----------------:|:----:|:----:|:----:|:----:|:----:|:----:|:----:|:----:|:----:|:----:|:----:|:----:|:----:|
|
|
198
|
+
| 0.1.321+ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
199
|
+
| 0.1.304 - 0.1.320 | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
200
|
+
| 0.1.297 – 0.1.303 | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
201
|
+
| 0.1.260 – 0.1.296 | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
202
|
+
| 0.1.256 – 0.1.259 | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
203
|
+
| 0.1.248 – 0.1.255 | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
204
|
+
| 0.1.235 – 0.1.247 | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
205
|
+
| 0.1.141 – 0.1.234 | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
206
|
+
| 0.1.129 – 0.1.140 | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
207
|
+
| 0.1.122 – 0.1.128 | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
208
|
+
| 0.1.15 – 0.1.121 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
209
209
|
|
|
210
210
|
|
|
211
211
|
### `react-native-worklets` compatibility
|
|
212
212
|
|
|
213
|
-
| | 0.6.x | 0.7.x |
|
|
214
|
-
|
|
215
|
-
| 0.1.321+ | ❌ | ✅ |
|
|
216
|
-
| 0.1.308 – 0.1.320 | ✅ | ❌ |
|
|
213
|
+
| | 0.6.x | 0.7.x | 0.8.x | 0.9.x |
|
|
214
|
+
|:-----------------:|:-----:|:-----:|:-----:|:-----:|
|
|
215
|
+
| 0.1.321+ | ❌ | ✅ | ✅ | ✅ |
|
|
216
|
+
| 0.1.308 – 0.1.320 | ✅ | ❌ | ❌ | ❌ |
|
|
217
217
|
|
|
218
218
|
|
|
219
219
|
## License
|
|
@@ -48,6 +48,12 @@ private:
|
|
|
48
48
|
YGMeasureMode heightMode);
|
|
49
49
|
static YogaLayoutableShadowNode &
|
|
50
50
|
shadowNodeFromContext(YGNodeConstRef yogaNode);
|
|
51
|
+
|
|
52
|
+
// Persisted RCTMarkdownUtils instance shared across shadow node clones so
|
|
53
|
+
// that MarkdownParser's one-entry memo cache (keyed on text + parserId)
|
|
54
|
+
// survives repeated Yoga measure callbacks instead of being discarded on
|
|
55
|
+
// every call to applyMarkdownFormattingToTextInputState.
|
|
56
|
+
mutable std::shared_ptr<void> markdownUtils_;
|
|
51
57
|
};
|
|
52
58
|
|
|
53
59
|
} // namespace react
|
|
@@ -32,6 +32,13 @@ MarkdownTextInputDecoratorShadowNode::MarkdownTextInputDecoratorShadowNode(
|
|
|
32
32
|
ShadowNode const &sourceShadowNode,
|
|
33
33
|
ShadowNodeFragment const &fragment)
|
|
34
34
|
: ConcreteViewShadowNode(sourceShadowNode, fragment) {
|
|
35
|
+
// Carry the persisted RCTMarkdownUtils over from the source node so the
|
|
36
|
+
// MarkdownParser memo cache survives the frequent cloning that happens
|
|
37
|
+
// during layout and re-render cycles.
|
|
38
|
+
const auto &source =
|
|
39
|
+
static_cast<const MarkdownTextInputDecoratorShadowNode &>(sourceShadowNode);
|
|
40
|
+
markdownUtils_ = source.markdownUtils_;
|
|
41
|
+
|
|
35
42
|
initialize();
|
|
36
43
|
makeChildNodeMutable();
|
|
37
44
|
|
|
@@ -182,12 +189,20 @@ void MarkdownTextInputDecoratorShadowNode::applyMarkdownFormattingToTextInputSta
|
|
|
182
189
|
const auto defaultNSTextAttributes =
|
|
183
190
|
RCTNSTextAttributesFromTextAttributes(defaultTextAttributes);
|
|
184
191
|
|
|
185
|
-
//
|
|
192
|
+
// Lazily create and persist the RCTMarkdownUtils instance so the MarkdownParser
|
|
193
|
+
// one-entry memo cache (keyed on text + parserId) survives repeated Yoga measure
|
|
194
|
+
// callbacks. Previously a fresh utils/parser was allocated on every call,
|
|
195
|
+
// discarding the cache and forcing a full JSI re-parse each time.
|
|
196
|
+
if (!markdownUtils_) {
|
|
197
|
+
RCTMarkdownUtils *freshUtils = [[RCTMarkdownUtils alloc] init];
|
|
198
|
+
markdownUtils_ = std::shared_ptr<void>(
|
|
199
|
+
(__bridge_retained void *)freshUtils, [](void *p) { CFRelease(p); });
|
|
200
|
+
}
|
|
201
|
+
RCTMarkdownUtils *utils = (__bridge RCTMarkdownUtils *)markdownUtils_.get();
|
|
202
|
+
|
|
186
203
|
RCTMarkdownStyle *markdownStyle =
|
|
187
204
|
[[RCTMarkdownStyle alloc] initWithStruct:decoratorProps.markdownStyle];
|
|
188
|
-
|
|
189
|
-
[utils setMarkdownStyle:markdownStyle];
|
|
190
|
-
[utils setParserId:[NSNumber numberWithInt:decoratorProps.parserId]];
|
|
205
|
+
NSNumber *parserId = [NSNumber numberWithInt:decoratorProps.parserId];
|
|
191
206
|
|
|
192
207
|
// convert the attibuted string stored in state to
|
|
193
208
|
// NSAttributedString
|
|
@@ -228,7 +243,10 @@ void MarkdownTextInputDecoratorShadowNode::applyMarkdownFormattingToTextInputSta
|
|
|
228
243
|
|
|
229
244
|
// apply markdown
|
|
230
245
|
NSMutableAttributedString *newString = [nsAttributedString mutableCopy];
|
|
231
|
-
[utils applyMarkdownFormatting:newString
|
|
246
|
+
[utils applyMarkdownFormatting:newString
|
|
247
|
+
withDefaultTextAttributes:defaultNSTextAttributes
|
|
248
|
+
markdownStyle:markdownStyle
|
|
249
|
+
parserId:parserId];
|
|
232
250
|
|
|
233
251
|
// create a clone of the old TextInputState and update the
|
|
234
252
|
// attributed string box to point to the string with markdown
|
|
@@ -240,7 +258,10 @@ void MarkdownTextInputDecoratorShadowNode::applyMarkdownFormattingToTextInputSta
|
|
|
240
258
|
|
|
241
259
|
// apply markdown
|
|
242
260
|
NSMutableAttributedString *newString = [nsAttributedString mutableCopy];
|
|
243
|
-
[utils applyMarkdownFormatting:newString
|
|
261
|
+
[utils applyMarkdownFormatting:newString
|
|
262
|
+
withDefaultTextAttributes:defaultNSTextAttributes
|
|
263
|
+
markdownStyle:markdownStyle
|
|
264
|
+
parserId:parserId];
|
|
244
265
|
|
|
245
266
|
// create a clone of the old TextInputState and update the
|
|
246
267
|
// attributed string box to point to the string with markdown
|
package/apple/RCTMarkdownUtils.h
CHANGED
|
@@ -11,6 +11,15 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
11
11
|
- (void)applyMarkdownFormatting:(nonnull NSMutableAttributedString *)attributedString
|
|
12
12
|
withDefaultTextAttributes:(nonnull NSDictionary<NSAttributedStringKey, id> *)defaultTextAttributes;
|
|
13
13
|
|
|
14
|
+
// Atomically sets the style/parser and applies formatting under a single lock.
|
|
15
|
+
// Use this from the shadow node measure path, where one RCTMarkdownUtils
|
|
16
|
+
// instance is shared across shadow node clones and may be accessed from
|
|
17
|
+
// concurrent Fabric commits/layout passes.
|
|
18
|
+
- (void)applyMarkdownFormatting:(nonnull NSMutableAttributedString *)attributedString
|
|
19
|
+
withDefaultTextAttributes:(nonnull NSDictionary<NSAttributedStringKey, id> *)defaultTextAttributes
|
|
20
|
+
markdownStyle:(nonnull RCTMarkdownStyle *)markdownStyle
|
|
21
|
+
parserId:(nonnull NSNumber *)parserId;
|
|
22
|
+
|
|
14
23
|
@end
|
|
15
24
|
|
|
16
25
|
NS_ASSUME_NONNULL_END
|
|
@@ -34,4 +34,22 @@
|
|
|
34
34
|
withMarkdownStyle:_markdownStyle];
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
- (void)applyMarkdownFormatting:(nonnull NSMutableAttributedString *)attributedString
|
|
38
|
+
withDefaultTextAttributes:(nonnull NSDictionary<NSAttributedStringKey, id> *)defaultTextAttributes
|
|
39
|
+
markdownStyle:(nonnull RCTMarkdownStyle *)markdownStyle
|
|
40
|
+
parserId:(nonnull NSNumber *)parserId
|
|
41
|
+
{
|
|
42
|
+
// Keep the style/parserId assignment and the parse+format together under a
|
|
43
|
+
// single lock. The shadow node shares one instance across clones, and Fabric
|
|
44
|
+
// runs commits/layout optimistically on multiple threads, so without this the
|
|
45
|
+
// setters could interleave with another thread's parse/format and apply the
|
|
46
|
+
// wrong parserId/style for a frame. `@synchronized` is recursive, so nesting
|
|
47
|
+
// with `MarkdownParser`'s own `@synchronized(self)` in `parse:` is safe.
|
|
48
|
+
@synchronized (self) {
|
|
49
|
+
_markdownStyle = markdownStyle;
|
|
50
|
+
_parserId = parserId;
|
|
51
|
+
[self applyMarkdownFormatting:attributedString withDefaultTextAttributes:defaultTextAttributes];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
37
55
|
@end
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@expensify/react-native-live-markdown",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.328",
|
|
4
4
|
"description": "Drop-in replacement for React Native's TextInput component with Markdown formatting.",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/module/index",
|
|
@@ -64,12 +64,13 @@
|
|
|
64
64
|
"@commitlint/config-conventional": "^17.0.2",
|
|
65
65
|
"@evilmartians/lefthook": "^1.5.0",
|
|
66
66
|
"@lwc/eslint-plugin-lwc": "^1.7.2",
|
|
67
|
-
"@react-native-community/cli": "20.
|
|
68
|
-
"@react-native-community/cli-platform-android": "20.
|
|
69
|
-
"@react-native-community/cli-platform-ios": "20.
|
|
70
|
-
"@react-native/babel-preset": "0.
|
|
71
|
-
"@react-native/eslint-config": "0.
|
|
72
|
-
"@react-native/
|
|
67
|
+
"@react-native-community/cli": "20.1.0",
|
|
68
|
+
"@react-native-community/cli-platform-android": "20.1.0",
|
|
69
|
+
"@react-native-community/cli-platform-ios": "20.1.0",
|
|
70
|
+
"@react-native/babel-preset": "0.85.3",
|
|
71
|
+
"@react-native/eslint-config": "0.85.3",
|
|
72
|
+
"@react-native/eslint-plugin": "^0.85.3",
|
|
73
|
+
"@react-native/metro-config": "0.85.3",
|
|
73
74
|
"@release-it/conventional-changelog": "^5.0.0",
|
|
74
75
|
"@types/jest": "^29.5.14",
|
|
75
76
|
"@types/react": "^19.2.0",
|
|
@@ -89,6 +90,7 @@
|
|
|
89
90
|
"eslint-plugin-jsx-a11y": "^6.8.0",
|
|
90
91
|
"eslint-plugin-prettier": "^4.0.0",
|
|
91
92
|
"eslint-plugin-promise": "^6.1.1",
|
|
93
|
+
"eslint-plugin-react-native": "^5.0.0",
|
|
92
94
|
"eslint-plugin-tsdoc": "^0.2.17",
|
|
93
95
|
"expensify-common": "2.0.148",
|
|
94
96
|
"jest": "^29.6.3",
|
|
@@ -96,15 +98,15 @@
|
|
|
96
98
|
"nodemon": "^3.1.3",
|
|
97
99
|
"patch-package": "^8.0.0",
|
|
98
100
|
"prettier": "2.8.8",
|
|
99
|
-
"react": "19.2.
|
|
100
|
-
"react-dom": "19.2.
|
|
101
|
-
"react-native": "0.
|
|
101
|
+
"react": "19.2.3",
|
|
102
|
+
"react-dom": "19.2.3",
|
|
103
|
+
"react-native": "0.85.3",
|
|
102
104
|
"react-native-builder-bob": "^0.30.2",
|
|
103
105
|
"react-native-web": "^0.20.0",
|
|
106
|
+
"react-native-worklets": "0.9.1",
|
|
104
107
|
"release-it": "^15.0.0",
|
|
105
108
|
"turbo": "^1.10.7",
|
|
106
|
-
"typescript": "^5.8.3"
|
|
107
|
-
"react-native-worklets": "0.7.2"
|
|
109
|
+
"typescript": "^5.8.3"
|
|
108
110
|
},
|
|
109
111
|
"peerDependencies": {
|
|
110
112
|
"expensify-common": ">=2.0.148",
|
|
@@ -112,11 +114,6 @@
|
|
|
112
114
|
"react-native": "*",
|
|
113
115
|
"react-native-worklets": ">=0.7.0"
|
|
114
116
|
},
|
|
115
|
-
"overrides": {
|
|
116
|
-
"@expo/webpack-config": {
|
|
117
|
-
"expo": "^54.0.32"
|
|
118
|
-
}
|
|
119
|
-
},
|
|
120
117
|
"workspaces": [
|
|
121
118
|
"./example",
|
|
122
119
|
"./WebExample"
|