@tinglinzh/react-native-markdownview 0.3.0 → 0.5.0

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/app.plugin.js ADDED
@@ -0,0 +1 @@
1
+ module.exports = require('./plugin/index.js');
@@ -1,13 +1,33 @@
1
1
  /**
2
- * Old Architecture bridge.
2
+ * Old Architecture view manager — pure Objective-C implementation.
3
3
  *
4
- * React Native uses KVC to set these properties on the UIView returned by
5
- * RNMarkdownViewManager.view(). The property names must match exactly what
6
- * RNMarkdownView exposes via @objc.
4
+ * Framework targets don't support bridging headers, so we:
5
+ * 1. Keep this file as ObjC (no bridging header needed).
6
+ * 2. Import the auto-generated Swift header to access RNMarkdownView.
7
+ * 3. Delete the separate RNMarkdownViewManager.swift file.
8
+ *
9
+ * The Swift-generated header is emitted by the compiler as
10
+ * "react_native_markdownview-Swift.h" (pod name, hyphens → underscores).
7
11
  */
8
12
  #import <React/RCTViewManager.h>
13
+ #import "react_native_markdownview-Swift.h"
14
+
15
+ @interface RNMarkdownViewManager : RCTViewManager
16
+ @end
17
+
18
+ @implementation RNMarkdownViewManager
19
+
20
+ RCT_EXPORT_MODULE()
21
+
22
+ - (UIView *)view
23
+ {
24
+ return [[RNMarkdownView alloc] init];
25
+ }
9
26
 
10
- @interface RCT_EXTERN_MODULE(RNMarkdownViewManager, RCTViewManager)
27
+ + (BOOL)requiresMainQueueSetup
28
+ {
29
+ return YES;
30
+ }
11
31
 
12
32
  // ─── Content ────────────────────────────────────────────────────────────────
13
33
  /// Raw markdown string.
@@ -24,11 +44,11 @@ RCT_EXPORT_VIEW_PROPERTY(onLinkPress, RCTDirectEventBlock)
24
44
  RCT_EXPORT_VIEW_PROPERTY(onImagePress, RCTDirectEventBlock)
25
45
 
26
46
  /// Fires when lines are selected in a code block.
27
- /// Payload: { startLine: number, endLine: number, contents: string[], language?: string }
47
+ /// Payload: { startLine: number, endLine: number, contents: string, language: string }
28
48
  RCT_EXPORT_VIEW_PROPERTY(onLineSelection, RCTDirectEventBlock)
29
49
 
30
50
  /// Fires when the intrinsic content height changes.
31
- /// Payload: { contentSize: { width: number, height: number } }
51
+ /// Payload: { width: number, height: number }
32
52
  RCT_EXPORT_VIEW_PROPERTY(onContentSizeChange, RCTDirectEventBlock)
33
53
 
34
54
  @end
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinglinzh/react-native-markdownview",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "React Native wrapper for MarkdownView — native iOS/macOS markdown rendering with GFM, syntax highlighting, LaTeX math, and unified diff support",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -14,10 +14,13 @@
14
14
  "types": "./lib/typescript/src/index.d.ts"
15
15
  }
16
16
  },
17
+ "plugin": "app.plugin.js",
17
18
  "files": [
18
19
  "src",
19
20
  "lib",
20
21
  "ios",
22
+ "plugin",
23
+ "app.plugin.js",
21
24
  "react-native-markdownview.podspec",
22
25
  "Package.swift"
23
26
  ],
@@ -37,9 +40,15 @@
37
40
  "author": "",
38
41
  "license": "MIT",
39
42
  "peerDependencies": {
43
+ "@expo/config-plugins": ">=7.0.0",
40
44
  "react": "*",
41
45
  "react-native": "*"
42
46
  },
47
+ "peerDependenciesMeta": {
48
+ "@expo/config-plugins": {
49
+ "optional": true
50
+ }
51
+ },
43
52
  "devDependencies": {
44
53
  "@types/react": "^18.3.28",
45
54
  "react": "18.3.1",
@@ -0,0 +1,139 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Expo Config Plugin — adds MarkdownView as an SPM dependency to the
5
+ * host app's Xcode project during `expo prebuild`.
6
+ *
7
+ * Without this step the pod can't compile `import MarkdownView` because
8
+ * CocoaPods has no way to pull in Swift Package Manager-only packages.
9
+ *
10
+ * Usage in app.config.ts / app.json:
11
+ * plugins: ['@tinglinzh/react-native-markdownview']
12
+ */
13
+
14
+ const { withXcodeProject } = require('@expo/config-plugins');
15
+ const crypto = require('crypto');
16
+
17
+ const MARKDOWNVIEW_REPO = 'https://github.com/HumanInterfaceDesign/MarkdownView';
18
+ const MARKDOWNVIEW_BRANCH = 'main';
19
+ const PRODUCT_NAME = 'MarkdownView';
20
+
21
+ /**
22
+ * Deterministic 24-char uppercase hex UUID derived from a seed string.
23
+ * Using a fixed seed means `prebuild` is idempotent — re-running it won't
24
+ * add duplicate entries to the pbxproj file.
25
+ */
26
+ function uuid(seed) {
27
+ return crypto.createHash('sha256').update(seed).digest('hex').toUpperCase().slice(0, 24);
28
+ }
29
+
30
+ const PKG_REF_UUID = uuid('RNMarkdownView-XCRemoteSwiftPackageReference');
31
+ const PROD_DEP_UUID = uuid('RNMarkdownView-XCSwiftPackageProductDependency');
32
+ const BUILD_FILE_UUID = uuid('RNMarkdownView-PBXBuildFile');
33
+
34
+ /**
35
+ * @param {import('@expo/config-plugins').ExpoConfig} config
36
+ */
37
+ const withMarkdownViewSPM = (config) => {
38
+ return withXcodeProject(config, (modConfig) => {
39
+ const project = modConfig.modResults;
40
+ const pbx = project.hash.project.objects;
41
+
42
+ // ── Guard: already added? ──────────────────────────────────────────────
43
+ const existingRefs = pbx['XCRemoteSwiftPackageReference'] || {};
44
+ const alreadyAdded = Object.values(existingRefs).some(
45
+ (ref) =>
46
+ ref &&
47
+ typeof ref === 'object' &&
48
+ typeof ref.repositoryURL === 'string' &&
49
+ ref.repositoryURL.includes('MarkdownView'),
50
+ );
51
+ if (alreadyAdded) return modConfig;
52
+
53
+ // ── 1. XCRemoteSwiftPackageReference ──────────────────────────────────
54
+ pbx['XCRemoteSwiftPackageReference'] = pbx['XCRemoteSwiftPackageReference'] || {};
55
+ pbx['XCRemoteSwiftPackageReference'][PKG_REF_UUID] = {
56
+ isa: 'XCRemoteSwiftPackageReference',
57
+ repositoryURL: `"${MARKDOWNVIEW_REPO}"`,
58
+ requirement: { branch: MARKDOWNVIEW_BRANCH, kind: 'branch' },
59
+ };
60
+ pbx['XCRemoteSwiftPackageReference'][`${PKG_REF_UUID}_comment`] =
61
+ `XCRemoteSwiftPackageReference "${PRODUCT_NAME}"`;
62
+
63
+ // ── 2. XCSwiftPackageProductDependency ────────────────────────────────
64
+ pbx['XCSwiftPackageProductDependency'] = pbx['XCSwiftPackageProductDependency'] || {};
65
+ pbx['XCSwiftPackageProductDependency'][PROD_DEP_UUID] = {
66
+ isa: 'XCSwiftPackageProductDependency',
67
+ package: PKG_REF_UUID,
68
+ productName: PRODUCT_NAME,
69
+ };
70
+ pbx['XCSwiftPackageProductDependency'][`${PROD_DEP_UUID}_comment`] = PRODUCT_NAME;
71
+
72
+ // ── 3. PBXBuildFile ───────────────────────────────────────────────────
73
+ pbx['PBXBuildFile'] = pbx['PBXBuildFile'] || {};
74
+ pbx['PBXBuildFile'][BUILD_FILE_UUID] = {
75
+ isa: 'PBXBuildFile',
76
+ productRef: PROD_DEP_UUID,
77
+ };
78
+ pbx['PBXBuildFile'][`${BUILD_FILE_UUID}_comment`] = `${PRODUCT_NAME} in Frameworks`;
79
+
80
+ // ── 4. PBXProject.packageReferences ──────────────────────────────────
81
+ const projectSection = pbx['PBXProject'] || {};
82
+ const projectObj = Object.values(projectSection).find(
83
+ (v) => v && typeof v === 'object' && v.isa === 'PBXProject',
84
+ );
85
+ if (projectObj) {
86
+ projectObj.packageReferences = projectObj.packageReferences || [];
87
+ if (!projectObj.packageReferences.some((r) => r.value === PKG_REF_UUID)) {
88
+ projectObj.packageReferences.push({
89
+ value: PKG_REF_UUID,
90
+ comment: `XCRemoteSwiftPackageReference "${PRODUCT_NAME}"`,
91
+ });
92
+ }
93
+ }
94
+
95
+ // ── 5. Add to the main app target ─────────────────────────────────────
96
+ const targetName = modConfig.modRequest.projectName;
97
+ const nativeTargets = pbx['PBXNativeTarget'] || {};
98
+
99
+ for (const [, target] of Object.entries(nativeTargets)) {
100
+ if (!target || typeof target !== 'object') continue;
101
+
102
+ // pbxproj stores names with surrounding quotes; handle both forms.
103
+ const name = typeof target.name === 'string'
104
+ ? target.name.replace(/^"|"$/g, '')
105
+ : '';
106
+ if (name !== targetName) continue;
107
+
108
+ // packageProductDependencies
109
+ target.packageProductDependencies = target.packageProductDependencies || [];
110
+ if (!target.packageProductDependencies.some((d) => d.value === PROD_DEP_UUID)) {
111
+ target.packageProductDependencies.push({
112
+ value: PROD_DEP_UUID,
113
+ comment: PRODUCT_NAME,
114
+ });
115
+ }
116
+
117
+ // Add build file to PBXFrameworksBuildPhase
118
+ for (const phaseRef of target.buildPhases || []) {
119
+ const frameworksSection = pbx['PBXFrameworksBuildPhase'] || {};
120
+ const phase = frameworksSection[phaseRef.value];
121
+ if (!phase) continue;
122
+
123
+ phase.files = phase.files || [];
124
+ if (!phase.files.some((f) => f.value === BUILD_FILE_UUID)) {
125
+ phase.files.push({
126
+ value: BUILD_FILE_UUID,
127
+ comment: `${PRODUCT_NAME} in Frameworks`,
128
+ });
129
+ }
130
+ break;
131
+ }
132
+ break;
133
+ }
134
+
135
+ return modConfig;
136
+ });
137
+ };
138
+
139
+ module.exports = withMarkdownViewSPM;
@@ -15,11 +15,11 @@ Pod::Spec.new do |s|
15
15
  s.source_files = "ios/**/*.{h,m,mm,swift}"
16
16
  s.swift_version = "5.9"
17
17
 
18
- # Exposes React Objective-C headers to Swift files inside this pod.
18
+ # Allow the Swift compiler to find the MarkdownView module when it is added
19
+ # to the host app via Xcode's Swift Package integration.
20
+ # Framework targets cannot use bridging headers; ObjC↔Swift interop is handled
21
+ # via the auto-generated "-Swift.h" header imported in RNMarkdownViewManager.m.
19
22
  s.pod_target_xcconfig = {
20
- "SWIFT_OBJC_BRIDGING_HEADER" => "$(PODS_TARGET_SRCROOT)/ios/react-native-markdownview-Bridging-Header.h",
21
- # Allow the Swift compiler to find the MarkdownView Swift module when it is
22
- # added to the host app via SPM (Xcode embeds SPM-built modules here).
23
23
  "SWIFT_INCLUDE_PATHS" => "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/PackageFrameworks/**",
24
24
  }
25
25
 
@@ -1,16 +0,0 @@
1
- import React
2
- import UIKit
3
-
4
- /// Old Architecture view manager.
5
- /// The ObjC bridge in RNMarkdownViewManager.m exports props and this class.
6
- @objc(RNMarkdownViewManager)
7
- final class RNMarkdownViewManager: RCTViewManager {
8
-
9
- override func view() -> UIView! {
10
- RNMarkdownView()
11
- }
12
-
13
- override static func requiresMainQueueSetup() -> Bool {
14
- true
15
- }
16
- }
@@ -1,12 +0,0 @@
1
- /**
2
- * Bridging header for Swift ↔ Objective-C interop within this pod.
3
- *
4
- * The CocoaPods build system picks up SWIFT_OBJC_BRIDGING_HEADER via
5
- * the pod_target_xcconfig in the podspec and makes these React Native
6
- * Objective-C headers visible to all Swift source files in the pod.
7
- */
8
- #import <React/RCTViewManager.h>
9
- #import <React/RCTBridgeModule.h>
10
- #import <React/RCTEventEmitter.h>
11
- #import <React/RCTView.h>
12
- #import <React/RCTUIManager.h>