Skip to content

Commit

Permalink
Use Hermes instead of JSC on iOS (#404)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomekzaw authored Jun 28, 2024
1 parent 1153e0d commit 5852938
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 137 deletions.
2 changes: 2 additions & 0 deletions RNLiveMarkdown.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Pod::Spec.new do |s|

s.resources = "parser/react-native-live-markdown-parser.js"

s.dependency "hermes-engine"

install_modules_dependencies(s)

if ENV['USE_FRAMEWORKS'] && ENV['RCT_NEW_ARCH_ENABLED']
Expand Down
110 changes: 6 additions & 104 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
PODS:
- boost (1.83.0)
- CocoaAsyncSocket (7.6.5)
- DoubleConversion (1.1.6)
- FBLazyVector (0.73.4)
- FBReactNativeSpec (0.73.4):
Expand All @@ -10,69 +9,12 @@ PODS:
- React-Core (= 0.73.4)
- React-jsi (= 0.73.4)
- ReactCommon/turbomodule/core (= 0.73.4)
- Flipper (0.201.0):
- Flipper-Folly (~> 2.6)
- Flipper-Boost-iOSX (1.76.0.1.11)
- Flipper-DoubleConversion (3.2.0.1)
- Flipper-Fmt (7.1.7)
- Flipper-Folly (2.6.10):
- Flipper-Boost-iOSX
- Flipper-DoubleConversion
- Flipper-Fmt (= 7.1.7)
- Flipper-Glog
- libevent (~> 2.1.12)
- OpenSSL-Universal (= 1.1.1100)
- Flipper-Glog (0.5.0.5)
- Flipper-PeerTalk (0.0.4)
- FlipperKit (0.201.0):
- FlipperKit/Core (= 0.201.0)
- FlipperKit/Core (0.201.0):
- Flipper (~> 0.201.0)
- FlipperKit/CppBridge
- FlipperKit/FBCxxFollyDynamicConvert
- FlipperKit/FBDefines
- FlipperKit/FKPortForwarding
- SocketRocket (~> 0.6.0)
- FlipperKit/CppBridge (0.201.0):
- Flipper (~> 0.201.0)
- FlipperKit/FBCxxFollyDynamicConvert (0.201.0):
- Flipper-Folly (~> 2.6)
- FlipperKit/FBDefines (0.201.0)
- FlipperKit/FKPortForwarding (0.201.0):
- CocoaAsyncSocket (~> 7.6)
- Flipper-PeerTalk (~> 0.0.4)
- FlipperKit/FlipperKitHighlightOverlay (0.201.0)
- FlipperKit/FlipperKitLayoutHelpers (0.201.0):
- FlipperKit/Core
- FlipperKit/FlipperKitHighlightOverlay
- FlipperKit/FlipperKitLayoutTextSearchable
- FlipperKit/FlipperKitLayoutIOSDescriptors (0.201.0):
- FlipperKit/Core
- FlipperKit/FlipperKitHighlightOverlay
- FlipperKit/FlipperKitLayoutHelpers
- FlipperKit/FlipperKitLayoutPlugin (0.201.0):
- FlipperKit/Core
- FlipperKit/FlipperKitHighlightOverlay
- FlipperKit/FlipperKitLayoutHelpers
- FlipperKit/FlipperKitLayoutIOSDescriptors
- FlipperKit/FlipperKitLayoutTextSearchable
- FlipperKit/FlipperKitLayoutTextSearchable (0.201.0)
- FlipperKit/FlipperKitNetworkPlugin (0.201.0):
- FlipperKit/Core
- FlipperKit/FlipperKitReactPlugin (0.201.0):
- FlipperKit/Core
- FlipperKit/FlipperKitUserDefaultsPlugin (0.201.0):
- FlipperKit/Core
- FlipperKit/SKIOSNetworkPlugin (0.201.0):
- FlipperKit/Core
- FlipperKit/FlipperKitNetworkPlugin
- fmt (6.2.1)
- glog (0.3.5)
- hermes-engine (0.73.4):
- hermes-engine/Pre-built (= 0.73.4)
- hermes-engine/Pre-built (0.73.4)
- libevent (2.1.12)
- OpenSSL-Universal (1.1.1100)
- RCT-Folly (2022.05.16.00):
- boost
- DoubleConversion
Expand Down Expand Up @@ -1111,13 +1053,15 @@ PODS:
- React-jsi (= 0.73.4)
- React-logger (= 0.73.4)
- React-perflogger (= 0.73.4)
- RNLiveMarkdown (0.1.79):
- RNLiveMarkdown (0.1.92):
- glog
- hermes-engine
- RCT-Folly (= 2022.05.16.00)
- React-Core
- RNLiveMarkdown/common (= 0.1.79)
- RNLiveMarkdown/common (0.1.79):
- RNLiveMarkdown/common (= 0.1.92)
- RNLiveMarkdown/common (0.1.92):
- glog
- hermes-engine
- RCT-Folly (= 2022.05.16.00)
- React-Core
- SocketRocket (0.6.1)
Expand All @@ -1128,30 +1072,9 @@ DEPENDENCIES:
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
- FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`)
- Flipper (= 0.201.0)
- Flipper-Boost-iOSX (= 1.76.0.1.11)
- Flipper-DoubleConversion (= 3.2.0.1)
- Flipper-Fmt (= 7.1.7)
- Flipper-Folly (= 2.6.10)
- Flipper-Glog (= 0.5.0.5)
- Flipper-PeerTalk (= 0.0.4)
- FlipperKit (= 0.201.0)
- FlipperKit/Core (= 0.201.0)
- FlipperKit/CppBridge (= 0.201.0)
- FlipperKit/FBCxxFollyDynamicConvert (= 0.201.0)
- FlipperKit/FBDefines (= 0.201.0)
- FlipperKit/FKPortForwarding (= 0.201.0)
- FlipperKit/FlipperKitHighlightOverlay (= 0.201.0)
- FlipperKit/FlipperKitLayoutPlugin (= 0.201.0)
- FlipperKit/FlipperKitLayoutTextSearchable (= 0.201.0)
- FlipperKit/FlipperKitNetworkPlugin (= 0.201.0)
- FlipperKit/FlipperKitReactPlugin (= 0.201.0)
- FlipperKit/FlipperKitUserDefaultsPlugin (= 0.201.0)
- FlipperKit/SKIOSNetworkPlugin (= 0.201.0)
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`)
- libevent (~> 2.1.12)
- OpenSSL-Universal (= 1.1.1100)
- RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
- RCT-Folly/Fabric (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
- RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
Expand All @@ -1160,7 +1083,6 @@ DEPENDENCIES:
- React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`)
- React-Codegen (from `build/generated/ios`)
- React-Core (from `../node_modules/react-native/`)
- React-Core/DevSupport (from `../node_modules/react-native/`)
- React-Core/RCTWebSocket (from `../node_modules/react-native/`)
- React-CoreModules (from `../node_modules/react-native/React/CoreModules`)
- React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`)
Expand Down Expand Up @@ -1201,18 +1123,8 @@ DEPENDENCIES:

SPEC REPOS:
trunk:
- CocoaAsyncSocket
- Flipper
- Flipper-Boost-iOSX
- Flipper-DoubleConversion
- Flipper-Fmt
- Flipper-Folly
- Flipper-Glog
- Flipper-PeerTalk
- FlipperKit
- fmt
- libevent
- OpenSSL-Universal
- SocketRocket

EXTERNAL SOURCES:
Expand Down Expand Up @@ -1318,23 +1230,13 @@ EXTERNAL SOURCES:

SPEC CHECKSUMS:
boost: d3f49c53809116a5d38da093a8aa78bf551aed09
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
DoubleConversion: fea03f2699887d960129cc54bba7e52542b6f953
FBLazyVector: 84f6edbe225f38aebd9deaf1540a4160b1f087d7
FBReactNativeSpec: d0086a479be91c44ce4687a962956a352d2dc697
Flipper: c7a0093234c4bdd456e363f2f19b2e4b27652d44
Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c
Flipper-DoubleConversion: 2dc99b02f658daf147069aad9dbd29d8feb06d30
Flipper-Fmt: 60cbdd92fc254826e61d669a5d87ef7015396a9b
Flipper-Folly: 584845625005ff068a6ebf41f857f468decd26b3
Flipper-Glog: 70c50ce58ddaf67dc35180db05f191692570f446
Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9
FlipperKit: 37525a5d056ef9b93d1578e04bc3ea1de940094f
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
glog: c5d68082e772fa1c511173d6b30a9de2c05a69a2
hermes-engine: b2669ce35fc4ac14f523b307aff8896799829fe2
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c
RCT-Folly: 7169b2b1c44399c76a47b5deaaba715eeeb476c0
RCTRequired: ab7f915c15569f04a49669e573e6e319a53f9faa
RCTTypeSafety: 63b97ced7b766865057e7154db0e81ce4ee6cf1e
Expand Down Expand Up @@ -1376,7 +1278,7 @@ SPEC CHECKSUMS:
React-runtimescheduler: ed48e5faac6751e66ee1261c4bd01643b436f112
React-utils: 6e5ad394416482ae21831050928ae27348f83487
ReactCommon: 840a955d37b7f3358554d819446bffcf624b2522
RNLiveMarkdown: a4ddf419a109cd3f916db22ee19f8b8293b4f7e4
RNLiveMarkdown: c04da6410e95af38ccb615b8de8bb4eefc9f6e2c
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
Yoga: 64cd2a583ead952b0315d5135bf39e053ae9be70

Expand Down
77 changes: 44 additions & 33 deletions ios/RCTMarkdownUtils.mm
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
#import "react_native_assert.h"
#import <React/RCTAssert.h>
#import <React/RCTFont.h>
#import <JavaScriptCore/JavaScriptCore.h>

#include <jsi/jsi.h>
#include <hermes/hermes.h>

using namespace facebook;

@implementation RCTMarkdownUtils {
NSString *_prevInputString;
Expand All @@ -23,20 +27,26 @@ - (NSAttributedString *)parseMarkdown:(nullable NSAttributedString *)input withA
return _prevAttributedString;
}

static JSContext *ctx = nil;
static JSValue *function = nil;
if (ctx == nil) {
static std::shared_ptr<jsi::Runtime> runtime;
if (runtime == nullptr) {
NSString *path = [[NSBundle mainBundle] pathForResource:@"react-native-live-markdown-parser" ofType:@"js"];
assert(path != nil && "[react-native-live-markdown] Markdown parser bundle not found");
NSString *content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
assert(content != nil && "[react-native-live-markdown] Markdown parser bundle is empty");
ctx = [[JSContext alloc] init];
[ctx evaluateScript:content];
function = ctx[@"parseExpensiMarkToRanges"];
runtime = facebook::hermes::makeHermesRuntime();
auto codeBuffer = std::make_shared<const jsi::StringBuffer>([content UTF8String]);
runtime->evaluateJavaScript(codeBuffer, "evaluateJavaScript");
}

JSValue *result = [function callWithArguments:@[inputString]];
NSArray *ranges = [result toArray];
jsi::Runtime &rt = *runtime;
auto text = jsi::String::createFromUtf8(rt, [inputString UTF8String]);

auto func = rt.global().getPropertyAsFunction(rt, "parseExpensiMarkToRanges");
auto output = func.call(rt, text);
if (output.isUndefined()) {
return input;
}
const auto &ranges = output.asObject(rt).asArray(rt);

NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:inputString attributes:attributes];
[attributedString beginEditing];
Expand All @@ -48,42 +58,43 @@ - (NSAttributedString *)parseMarkdown:(nullable NSAttributedString *)input withA

_blockquoteRangesAndLevels = [NSMutableArray new];

[ranges enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSDictionary *item = obj;
NSString *type = [item valueForKey:@"type"];
NSInteger location = [[item valueForKey:@"start"] unsignedIntegerValue];
NSInteger length = [[item valueForKey:@"length"] unsignedIntegerValue];
NSInteger depth = [[item valueForKey:@"depth"] unsignedIntegerValue] ?: 1;
for (size_t i = 0, n = ranges.size(rt); i < n; ++i) {
const auto &item = ranges.getValueAtIndex(rt, i).asObject(rt);
const auto &type = item.getProperty(rt, "type").asString(rt).utf8(rt);
const auto &location = static_cast<int>(item.getProperty(rt, "start").asNumber());
const auto &length = static_cast<int>(item.getProperty(rt, "length").asNumber());
const auto &depth = item.hasProperty(rt, "depth") ? static_cast<int>(item.getProperty(rt, "depth").asNumber()) : 1;

NSRange range = NSMakeRange(location, length);

if ([type isEqualToString:@"bold"] || [type isEqualToString:@"italic"] || [type isEqualToString:@"code"] || [type isEqualToString:@"pre"] || [type isEqualToString:@"h1"] || [type isEqualToString:@"emoji"]) {
if (type == "bold" || type == "italic" || type == "code" || type == "pre" || type == "h1" || type == "emoji") {
UIFont *font = [attributedString attribute:NSFontAttributeName atIndex:location effectiveRange:NULL];
if ([type isEqualToString:@"bold"]) {
if (type == "bold") {
font = [RCTFont updateFont:font withWeight:@"bold"];
} else if ([type isEqualToString:@"italic"]) {
} else if (type == "italic") {
font = [RCTFont updateFont:font withStyle:@"italic"];
} else if ([type isEqualToString:@"code"]) {
} else if (type == "code") {
font = [RCTFont updateFont:font withFamily:_markdownStyle.codeFontFamily
size:[NSNumber numberWithFloat:_markdownStyle.codeFontSize]
weight:nil
style:nil
variant:nil
scaleMultiplier:0];
} else if ([type isEqualToString:@"pre"]) {
} else if (type == "pre") {
font = [RCTFont updateFont:font withFamily:_markdownStyle.preFontFamily
size:[NSNumber numberWithFloat:_markdownStyle.preFontSize]
weight:nil
style:nil
variant:nil
scaleMultiplier:0];
} else if ([type isEqualToString:@"h1"]) {
} else if (type == "h1") {
font = [RCTFont updateFont:font withFamily:nil
size:[NSNumber numberWithFloat:_markdownStyle.h1FontSize]
weight:@"bold"
style:nil
variant:nil
scaleMultiplier:0];
} else if ([type isEqualToString:@"emoji"]) {
} else if (type == "emoji") {
font = [RCTFont updateFont:font withFamily:nil
size:[NSNumber numberWithFloat:_markdownStyle.emojiFontSize]
weight:nil
Expand All @@ -94,27 +105,27 @@ - (NSAttributedString *)parseMarkdown:(nullable NSAttributedString *)input withA
[attributedString addAttribute:NSFontAttributeName value:font range:range];
}

if ([type isEqualToString:@"syntax"]) {
if (type == "syntax") {
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.syntaxColor range:range];
} else if ([type isEqualToString:@"strikethrough"]) {
} else if (type == "strikethrough") {
[attributedString addAttribute:NSStrikethroughStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleSingle] range:range];
} else if ([type isEqualToString:@"code"]) {
} else if (type == "code") {
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.codeColor range:range];
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.codeBackgroundColor range:range];
} else if ([type isEqualToString:@"mention-here"]) {
} else if (type == "mention-here") {
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.mentionHereColor range:range];
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.mentionHereBackgroundColor range:range];
} else if ([type isEqualToString:@"mention-user"]) {
} else if (type == "mention-user") {
// TODO: change mention color when it mentions current user
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.mentionUserColor range:range];
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.mentionUserBackgroundColor range:range];
} else if ([type isEqualToString:@"mention-report"]) {
} else if (type == "mention-report") {
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.mentionReportColor range:range];
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.mentionReportBackgroundColor range:range];
} else if ([type isEqualToString:@"link"]) {
} else if (type == "link") {
[attributedString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleSingle] range:range];
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.linkColor range:range];
} else if ([type isEqualToString:@"blockquote"]) {
} else if (type == "blockquote") {
CGFloat indent = (_markdownStyle.blockquoteMarginLeft + _markdownStyle.blockquoteBorderWidth + _markdownStyle.blockquotePaddingLeft) * depth;
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
paragraphStyle.firstLineHeadIndent = indent;
Expand All @@ -124,17 +135,17 @@ - (NSAttributedString *)parseMarkdown:(nullable NSAttributedString *)input withA
@"range": [NSValue valueWithRange:range],
@"depth": @(depth)
}];
} else if ([type isEqualToString:@"pre"]) {
} else if (type == "pre") {
[attributedString addAttribute:NSForegroundColorAttributeName value:_markdownStyle.preColor range:range];
NSRange rangeForBackground = [inputString characterAtIndex:range.location] == '\n' ? NSMakeRange(range.location + 1, range.length - 1) : range;
[attributedString addAttribute:NSBackgroundColorAttributeName value:_markdownStyle.preBackgroundColor range:rangeForBackground];
// TODO: pass background color and ranges to layout manager
} else if ([type isEqualToString:@"h1"]) {
} else if (type == "h1") {
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
NSRange rangeWithHashAndSpace = NSMakeRange(range.location - 2, range.length + 2); // we also need to include prepending "# "
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:rangeWithHashAndSpace];
}
}];
}

RCTApplyBaselineOffset(attributedString);

Expand Down

0 comments on commit 5852938

Please sign in to comment.