diff --git a/packages/core/hooks/android/extractors/class-declaration.ts b/packages/core/hooks/android/extractors/class-declaration.ts index e119eb5..690b351 100644 --- a/packages/core/hooks/android/extractors/class-declaration.ts +++ b/packages/core/hooks/android/extractors/class-declaration.ts @@ -40,6 +40,7 @@ export function extractClassDeclarationForModule(file: string) { file.match( /class\s+(\w+[^(\s]*)[\s\w():]*(\s+implements\s+|:)[\s\w():,]*[^{]*TurboModule/ ); + if (turboModuleMatch) return turboModuleMatch; const viewManagerMatch = @@ -66,10 +67,18 @@ export function extractClassDeclarationForModule(file: string) { ); if (moduleMatch) return moduleMatch; - - const ktModuleMatch = file.match( - /class\s+(\w+)(\s+|)\(.*\)(\s+|):(\s+|)ReactContextBaseJavaModule/gm + const specModuleMatch = file.match( + /public class\s+(\w+[^(\s]*)[\s\w():]*(\s+extends\s+|:)[\s\w():,]*[^{]*(\w+Spec)\s/ ); + if (specModuleMatch) { + return specModuleMatch; + } + + let ktModuleMatch = + file.match( + /class\s+(\w+)(\s+|)\(.*\)(\s+|):(\s+|)ReactContextBaseJavaModule/gm + ) || + file.match(/class\s+(\w+)(\s+|)\(.*\)(\s+|):(\s+|)(\w+Spec)/g); return ktModuleMatch; } diff --git a/packages/core/hooks/android/extractors/modules.ts b/packages/core/hooks/android/extractors/modules.ts index b583e30..3c979a2 100644 --- a/packages/core/hooks/android/extractors/modules.ts +++ b/packages/core/hooks/android/extractors/modules.ts @@ -60,11 +60,12 @@ export async function extractPackageModules(folder: string) { const moduleDeclarationMatch = extractClassDeclarationForModule( file.contents ); + if (moduleDeclarationMatch) { const [moduleClassSignature] = moduleDeclarationMatch; - + const superclassName = moduleClassSignature.match( - /(?:extends\s+|:)(\w+)/ + /(?:extends\s+|\s+:\s+)(\w+)/ )?.[1]; // if (!superclassName) { // continue; diff --git a/packages/core/hooks/ios/extractors/interface.ts b/packages/core/hooks/ios/extractors/interface.ts index fb06059..cae959b 100644 --- a/packages/core/hooks/ios/extractors/interface.ts +++ b/packages/core/hooks/ios/extractors/interface.ts @@ -10,6 +10,7 @@ import { extractModuleAliasedName } from './module-aliased-name'; * macros (e.g. RCT_EXPORT_METHOD). */ export function extractInterfaces(sourceCode: string, sourceFiles: string[]) { + /** * Every swift module interface file should have this piece of code. */ @@ -36,14 +37,20 @@ export function extractInterfaces(sourceCode: string, sourceFiles: string[]) { * ] * } */ + + const moduleNamesToMethodDescriptions = [ ...sourceCode.matchAll( isSwiftModuleInterface ? /\s*@interface\s+RCT_EXTERN_MODULE\(\s*([A-z0-9$]+),\s+(?:.|[\r\n])*?@end/gm : /\s*@implementation\s+([A-z0-9$]+)\s+(?:.|[\r\n])*?@end/gm ), + ...sourceCode.matchAll( + /\s*@interface\s+RCT_EXTERN_MODULE\s+\(\s*([A-z0-9$]+),\s+(?:.|[\r\n])*?@end/gm + ), ].reduce((acc, matches) => { const [match, objcClassName] = matches; + if (!objcClassName) { return acc; } @@ -74,10 +81,12 @@ export function extractInterfaces(sourceCode: string, sourceFiles: string[]) { '' ); + if (!exportedModuleName) { return acc; } + /** * Extract the signatures of any methods registered using RCT_EXTERN_METHOD. * @example @@ -227,11 +236,11 @@ export function extractInterfaces(sourceCode: string, sourceFiles: string[]) { ...quickExportedMethods, ]; - if (!allMethods.length) { - console.warn( - `${logPrefix} Unable to extract any methods from RCTBridgeModule named "${exportedModuleName}".` - ); - } + // if (!allMethods.length) { + // console.warn( + // `${logPrefix} Unable to extract any methods from RCTBridgeModule named "${exportedModuleName}".` + // ); + // } const exportsConstants = isSwiftModuleInterface ? swiftImplContents?.includes('func constantsToExport()') || false diff --git a/packages/core/hooks/ios/getters/autolink-info.ts b/packages/core/hooks/ios/getters/autolink-info.ts index 986f2af..8d07bca 100644 --- a/packages/core/hooks/ios/getters/autolink-info.ts +++ b/packages/core/hooks/ios/getters/autolink-info.ts @@ -112,14 +112,20 @@ export async function getPackageAutolinkInfo({ const subspecPaths = []; const subspecs = {}; - if (packageConfig?.podSubspecs) { - for (const subspecName of packageConfig.podSubspecs) { + + const podSubspecs = packageConfig?.podSubspecs || (podspecParsed['subspecs']?.length === 1 && !podspecHasSourceFiles ? podspecParsed['subspecs'].map(name => name.name) : []); + if (podSubspecs) { + for (const subspecName of podSubspecs) { const subspec = podspecParsed['subspecs']?.find( (subspec) => subspec.name === subspecName ); if (subspec) { subspecs[subspecName] = subspec; - subspecPaths.push(...subspec.source_files); + if (typeof subspec.source_files === 'string') { + subspecPaths.push(subspec.source_files); + } else { + subspecPaths.push(...subspec.source_files); + } } else { console.warn( `${logPrefix} Subspec ${subspecName} was not found in "${podspecFileName}" for npm package "${packageName}".` @@ -136,6 +142,7 @@ export async function getPackageAutolinkInfo({ iosSourceFiles, cwd: path.dirname(podspecFilePath), }); + // In a complicated pod setup, e.g. with subspecs, there may be special cases // to handle. In practice, this is redundant for core modules (we don't // augment the header at present), but it's good to be prepared for a future diff --git a/packages/core/index.d.ts b/packages/core/index.d.ts index 39700fe..1f7b78c 100644 --- a/packages/core/index.d.ts +++ b/packages/core/index.d.ts @@ -8,20 +8,37 @@ export type { } from './Libraries/Utilities/Platform'; export { Linking } from './Libraries/Linking/Linking'; import EventEmitter from './Libraries/vendor/emitter/EventEmitter'; +import { View } from '@nativescript/core'; export { AppRegistry } from './Libraries/ReactNative/AppRegistry'; export { Alert } from './Libraries/Alert/Alert'; export { ViewManagersIOS, requireNativeViewIOS } from './src/ios/viewmanagers'; -export {BatchedBridge} from "./Libraries/BatchedBridge/BatchedBridge"; +export { BatchedBridge } from './Libraries/BatchedBridge/BatchedBridge'; export { ViewManagersAndroid, requireNativeViewAndroid, } from './src/android/viewmanagers'; -export { requireNativeComponent } from './common'; +export type OpenNativeBaseView = View & { + nativeProps: { [name: string]: any }; + _viewTag: number; + _viewManager: { + setNativeProp(view: any, prop: string, ...params: any[]): void; + }; + commands: { + [K in keyof Commands]: (...args: any[]) => void; + }; + prototype: OpenNativeBaseView; + new (): OpenNativeBaseView; +} & Props & + Commands; + +export function requireNativeComponent( + componentName: string +): OpenNativeBaseView<{}, {}>; export type NativeSyntheticEvent = { - nativeEvent: T -} + nativeEvent: T; +}; declare global { // eslint-disable-next-line no-var diff --git a/packages/core/index.ios.ts b/packages/core/index.ios.ts index 86aee49..b7bb8a7 100644 --- a/packages/core/index.ios.ts +++ b/packages/core/index.ios.ts @@ -1,21 +1,21 @@ import RCTDeviceEventEmitter from './Libraries/EventEmitter/RCTDeviceEventEmitter'; import * as _TurboModuleRegistry from './Libraries/TurboModule/TurboModuleRegistry'; import { load } from './src/ios/nativemodules'; +import { + load as loadViewManagers, + requireNativeViewIOS, +} from './src/ios/viewmanagers'; +export { Alert } from './Libraries/Alert/Alert'; +export { BatchedBridge } from "./Libraries/BatchedBridge/BatchedBridge"; export { default as NativeEventEmitter } from './Libraries/EventEmitter/NativeEventEmitter'; export { Linking } from './Libraries/Linking/Linking'; export { AppRegistry } from './Libraries/ReactNative/AppRegistry'; export type { TurboModule } from './Libraries/TurboModule/RCTExport'; +export { Dimensions } from './Libraries/Utilities/Dimensions'; export { Platform } from './Libraries/Utilities/Platform'; export { NativeModules } from './src/ios/nativemodules'; -export const DeviceEventEmitter = RCTDeviceEventEmitter; -export {BatchedBridge} from "./Libraries/BatchedBridge/BatchedBridge"; -export { Dimensions } from './Libraries/Utilities/Dimensions'; -export { Alert } from './Libraries/Alert/Alert'; export { ViewManagersIOS, requireNativeViewIOS } from './src/ios/viewmanagers'; -import { - load as loadViewManagers, - requireNativeViewIOS, -} from './src/ios/viewmanagers'; +export const DeviceEventEmitter = RCTDeviceEventEmitter; export function requireNativeComponent(viewName: string) { return requireNativeViewIOS(viewName as never); diff --git a/packages/core/package-for-npm.json b/packages/core/package-for-npm.json index d8a1c00..ac6dde3 100644 --- a/packages/core/package-for-npm.json +++ b/packages/core/package-for-npm.json @@ -1,6 +1,6 @@ { "name": "@open-native/core", - "version": "2.0.0-alpha.22", + "version": "2.0.0-alpha.29", "description": "Open Native helps cross platform communities work better together.", "main": "index", "types": "index.d.ts", diff --git a/packages/core/package.json b/packages/core/package.json index 82f9c72..6e5e81d 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@open-native/core", - "version": "2.0.0-alpha.22", + "version": "2.0.0-alpha.29", "description": "Open Native helps cross platform communities work better together.", "main": "index", "types": "index.d.ts", diff --git a/packages/core/react-android/react/src/main/java/com/facebook/react/bridge/CatalystInstance.java b/packages/core/react-android/react/src/main/java/com/facebook/react/bridge/CatalystInstance.java index eb4247f..58d7662 100644 --- a/packages/core/react-android/react/src/main/java/com/facebook/react/bridge/CatalystInstance.java +++ b/packages/core/react-android/react/src/main/java/com/facebook/react/bridge/CatalystInstance.java @@ -7,6 +7,8 @@ package com.facebook.react.bridge; +import android.view.View; + import androidx.annotation.Nullable; import com.facebook.common.internal.DoNotStrip; @@ -37,6 +39,8 @@ public interface CatalystInstance { JSIModule getJSIModule(JSIModuleType moduleType); + View resolveView(int tag); + Collection getNativeModules(); @Deprecated diff --git a/packages/core/react-android/react/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java b/packages/core/react-android/react/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java new file mode 100644 index 0000000..50fd850 --- /dev/null +++ b/packages/core/react-android/react/src/main/java/com/facebook/react/uimanager/NativeViewHierarchyManager.java @@ -0,0 +1,19 @@ +package com.facebook.react.uimanager; + +import android.view.View; + +import com.facebook.react.bridge.ReactApplicationContext; +public class NativeViewHierarchyManager { + + public ReactApplicationContext reactApplicationContext; + + public NativeViewHierarchyManager(ReactApplicationContext reactApplicationContext) { + this.reactApplicationContext = reactApplicationContext; + } + + public final View resolveView(int tag){ + + return this.reactApplicationContext.getCatalystInstance().resolveView(tag); + } + +} diff --git a/packages/core/react-android/react/src/main/java/com/facebook/react/uimanager/UIBlock.java b/packages/core/react-android/react/src/main/java/com/facebook/react/uimanager/UIBlock.java new file mode 100644 index 0000000..6e8f44f --- /dev/null +++ b/packages/core/react-android/react/src/main/java/com/facebook/react/uimanager/UIBlock.java @@ -0,0 +1,5 @@ +package com.facebook.react.uimanager; + +public interface UIBlock { + public void execute(NativeViewHierarchyManager nativeViewHierarchyManager); +} diff --git a/packages/core/react-android/react/src/main/java/com/facebook/react/uimanager/UIManagerModule.java b/packages/core/react-android/react/src/main/java/com/facebook/react/uimanager/UIManagerModule.java index d918693..c1fdb1a 100644 --- a/packages/core/react-android/react/src/main/java/com/facebook/react/uimanager/UIManagerModule.java +++ b/packages/core/react-android/react/src/main/java/com/facebook/react/uimanager/UIManagerModule.java @@ -10,6 +10,7 @@ import android.view.View; +import androidx.annotation.Keep; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -34,8 +35,11 @@ public class UIManagerModule extends ReactContextBaseJavaModule implements UIMan public static final String NAME = "UIManager"; + private NativeViewHierarchyManager viewHierarchyManager; + public UIManagerModule(ReactApplicationContext context) { super(context); + viewHierarchyManager = new NativeViewHierarchyManager(this.getReactApplicationContext()); } @Override @@ -88,9 +92,16 @@ public void removeUIManagerEventListener(UIManagerListener listener) { } + public void addUIBlock(UIBlock block) { + block.execute(this.viewHierarchyManager); + } + public void prependUIBlock(UIBlock block) { + block.execute(this.viewHierarchyManager); + } + @Override public View resolveView(int reactTag) { - return null; + return this.viewHierarchyManager.resolveView(reactTag); } public void receiveEvent(int reactTag, String eventName, @Nullable WritableMap event) { diff --git a/packages/core/scripts/open-native.gradle b/packages/core/scripts/open-native.gradle index 9a07fba..12889a0 100644 --- a/packages/core/scripts/open-native.gradle +++ b/packages/core/scripts/open-native.gradle @@ -10,6 +10,7 @@ ext { targetSdkVersion = safeExtGetOpenNative("NS_DEFAULT_COMPILE_SDK_VERSION", 32) as int androidXCoreVersion = safeExtGetOpenNative("ns_default_androidx_core_version", "1.8.0") androidXCompatVersion = safeExtGetOpenNative("ns_default_androidx_appcompat_version", "1.5.1") + kotlinVersion = safeExtGetOpenNative("ns_default_kotlin_version", "1.7.10") } allprojects { diff --git a/packages/core/src/android/bridge.ts b/packages/core/src/android/bridge.ts index 2ddaa3f..396cebf 100644 --- a/packages/core/src/android/bridge.ts +++ b/packages/core/src/android/bridge.ts @@ -45,6 +45,10 @@ function RCTNativeAppEventEmitter() { const viewRegistry = {}; +export function getRegisteredViewForTag(tag: number) { + return viewRegistry[tag]; +} + function RCTEventEmitter() { return new com.facebook.react.uimanager.events.RCTEventEmitter({ receiveTouches(param0, param1, param2) { diff --git a/packages/core/src/android/catalyst-instance.ts b/packages/core/src/android/catalyst-instance.ts index 4a341f5..59851f1 100644 --- a/packages/core/src/android/catalyst-instance.ts +++ b/packages/core/src/android/catalyst-instance.ts @@ -1,3 +1,4 @@ +import { getRegisteredViewForTag } from './bridge'; import { toJSValue } from './converter'; import { JSModules } from './js-modules'; import { @@ -64,6 +65,9 @@ export default class CatalystInstance { module[method](...((toJSValue(args) || []) as [])); } }, + resolveView(param0) { + return getRegisteredViewForTag(param0) || null; + } }); } } diff --git a/packages/core/src/android/viewmanagers.ts b/packages/core/src/android/viewmanagers.ts index 0233073..cca78a0 100644 --- a/packages/core/src/android/viewmanagers.ts +++ b/packages/core/src/android/viewmanagers.ts @@ -267,31 +267,35 @@ export function requireNativeViewAndroid( ViewClass[event + 'Event'] = event; } - const commands = ( + const commandsMap = ( viewManager.nativeModule as com.facebook.react.uimanager.ViewManager< any, any > ).getCommandsMap(); - const commandNames = Utils.android.collections.stringSetToStringArray( - commands.keySet() - ); - for (const commandName of commandNames as string[]) { - ViewClass.prototype[commandName] = function (...args: any[]) { - if (!this.nativeViewProtected) return; - - ( - viewManager.nativeModule as com.facebook.react.uimanager.ViewManager< - any, - any - > - ).receiveCommand( - this.nativeViewProtected, - commandName, - toNativeValue(args || [], false) as ReadableArray - ); - }; + if (commandsMap) { + const commandNames = Utils.android.collections.stringSetToStringArray( + commandsMap.keySet() + ); + const commands = {}; + for (const commandName of commandNames as string[]) { + commands[commandName] = function (...args: any[]) { + if (!this.nativeViewProtected) return; + + ( + viewManager.nativeModule as com.facebook.react.uimanager.ViewManager< + any, + any + > + ).receiveCommand( + this.nativeViewProtected, + commandName, + toNativeValue(args || [], false) as ReadableArray + ); + }; + } + ViewClass.prototype['commands'] = commands; } for (const prop in viewManager.props) { @@ -348,7 +352,7 @@ export function requireNativeViewAndroid( const ViewManagerInstances = {}; const ViewManagerModules = {}; for (const module of getModuleClasses() as string[]) { - if (!module.startsWith("$$")) continue; + if (!module.startsWith('$$')) continue; Object.defineProperty(ViewManagerModules, module.slice(2), { get() { if (ViewManagerInstances[module]) return ViewManagerInstances[module]; diff --git a/packages/core/src/ios/viewmanagers.ts b/packages/core/src/ios/viewmanagers.ts index 29b7789..b9260d3 100644 --- a/packages/core/src/ios/viewmanagers.ts +++ b/packages/core/src/ios/viewmanagers.ts @@ -180,7 +180,7 @@ export function requireNativeViewIOS( createNativeView() { this._viewTag = this._viewManager.__getViewId(); const nativeView = this._viewManager._createViewInstance(); - this._viewManager.__registerView(this._viewTag, this); + this._viewManager.__registerView(this._viewTag, nativeView); return nativeView; } @@ -288,23 +288,19 @@ export function requireNativeViewIOS( for (const event of Object.keys(viewManager.viewEvents || {})) { NATIVE_VIEW_CACHE[key as string][event + 'Event'] = event; } - + const commands = {}; for (const method in viewManager.moduleMetadata.methods) { - const firstParamType = viewManager.moduleMetadata.methods[method].types[0]; - Object.defineProperty(NATIVE_VIEW_CACHE[key as string].prototype, method, { + Object.defineProperty(commands, method, { value: function (...args: any[]) { - if ( - firstParamType !== RNObjcSerialisableType.number && - firstParamType !== RNObjcSerialisableType.nonnullNumber - ) { - viewManager[method](...args); - } else { - viewManager[method](this._viewTag, ...args); - } + viewManager[method](...args); }, }); } + Object.defineProperty(NATIVE_VIEW_CACHE[key as string].prototype, 'commands', { + value: commands, + }) + for (const prop in viewManager.moduleMetadata.props) { Object.defineProperty(NATIVE_VIEW_CACHE[key as string].prototype, prop, { set(newValue) { diff --git a/packages/core/test_hook.js b/packages/core/test_hook.js index 2b96eb3..fc9ae91 100644 --- a/packages/core/test_hook.js +++ b/packages/core/test_hook.js @@ -29,7 +29,7 @@ require('../../apps/demo/scripts/before-prepare')({ dependencies: nsDemoPackageJson.dependencies, ignoredDependencies: [], projectDir: nsDemoProjectDir, - platform, + platform }, prepareData: { platform, diff --git a/packages/core/typings/android/android.d.ts b/packages/core/typings/android/android.d.ts index 2151354..7ebb241 100644 --- a/packages/core/typings/android/android.d.ts +++ b/packages/core/typings/android/android.d.ts @@ -944,6 +944,7 @@ declare module com { setTurboModuleManager( param0: com.facebook.react.bridge.JSIModule ): void; + resolveView(param0: number): android.view.View; callFunction( param0: string, param1: string,