diff --git a/package.json b/package.json index 6570a17..e459c64 100644 --- a/package.json +++ b/package.json @@ -4,12 +4,40 @@ "description": "Mendix native mobile package", "main": "./lib/module/index.js", "types": "./lib/typescript/src/index.d.ts", + "typesVersions": { + "*": { + "file-system": [ + "lib/typescript/src/file-system/index.d.ts" + ], + "native-modules": [ + "lib/typescript/src/native-modules/index.d.ts" + ], + "navigation-mode": [ + "lib/typescript/src/navigation-mode/index.d.ts" + ] + } + }, "exports": { ".": { "source": "./src/index.tsx", "types": "./lib/typescript/src/index.d.ts", "default": "./lib/module/index.js" }, + "./file-system": { + "source": "./src/file-system/index.ts", + "types": "./lib/typescript/src/file-system/index.d.ts", + "default": "./lib/module/file-system/index.js" + }, + "./native-modules": { + "source": "./src/native-modules/index.ts", + "types": "./lib/typescript/src/native-modules/index.d.ts", + "default": "./lib/module/native-modules/index.js" + }, + "./navigation-mode": { + "source": "./src/navigation-mode/index.ts", + "types": "./lib/typescript/src/navigation-mode/index.d.ts", + "default": "./lib/module/navigation-mode/index.js" + }, "./package.json": "./package.json" }, "files": [ diff --git a/src/file-system/NativeMxFileSystem.ts b/src/file-system/NativeMxFileSystem.ts index 69386a2..6439a62 100644 --- a/src/file-system/NativeMxFileSystem.ts +++ b/src/file-system/NativeMxFileSystem.ts @@ -1,8 +1,5 @@ -import { - TurboModuleRegistry, - type TurboModule, - type CodegenTypes, -} from 'react-native'; +import { TurboModuleRegistry } from 'react-native'; +import type { TurboModule, CodegenTypes } from 'react-native'; type BlobData = { blobId: string; @@ -34,6 +31,22 @@ export interface Spec extends TurboModule { setEncryptionEnabled(enabled: boolean): void; } -export default TurboModuleRegistry.getEnforcing('MxFileSystem'); +let cachedModule: Spec | undefined; +const getModule = (): Spec => { + if (!cachedModule) { + cachedModule = TurboModuleRegistry.getEnforcing('MxFileSystem'); + } + return cachedModule; +}; + +// Resolves the native module lazily (on first property access) instead of at import time, +// so requiring this file on web, where TurboModuleRegistry is stubbed out, never crashes. +const NativeMxFileSystem = new Proxy({} as Spec, { + get(_target, prop) { + return getModule()[prop as keyof Spec]; + }, +}); + +export default NativeMxFileSystem; export type { BlobData, FsConstants }; diff --git a/src/file-system/index.ts b/src/file-system/index.ts index 9106703..db16e85 100644 --- a/src/file-system/index.ts +++ b/src/file-system/index.ts @@ -44,4 +44,19 @@ const cast = (value: unknown): T | undefined => { return value as T; }; -export const NativeFileSystem = initFs(); +type Fs = ReturnType; +let cachedFs: Fs | undefined; +const getFs = (): Fs => { + if (!cachedFs) { + cachedFs = initFs(); + } + return cachedFs; +}; + +// Deferred so importing this module never touches the native file system module +// (e.g. on web, where it doesn't exist) until a consumer actually reads a property. +export const NativeFileSystem = new Proxy({} as Fs, { + get(_target, prop) { + return getFs()[prop as keyof Fs]; + }, +}); diff --git a/src/index.ts b/src/index.ts index c24aba9..aa96ec2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,3 +10,4 @@ export * from './encrypted-storage'; export * from './error'; export * from './file-system'; export * from './navigation-mode'; +export * from './native-modules'; diff --git a/src/native-modules/index.ts b/src/native-modules/index.ts new file mode 100644 index 0000000..b9822fc --- /dev/null +++ b/src/native-modules/index.ts @@ -0,0 +1,5 @@ +import { TurboModuleRegistry } from 'react-native'; + +export const getNativeModule = >( + name: string +): T | null => TurboModuleRegistry.get(name) ?? null; diff --git a/src/navigation-mode/NativeMxNavigation.ts b/src/navigation-mode/NativeMxNavigation.ts index eaa3dc3..4d6b2a2 100644 --- a/src/navigation-mode/NativeMxNavigation.ts +++ b/src/navigation-mode/NativeMxNavigation.ts @@ -1,9 +1,25 @@ -import { TurboModuleRegistry, type TurboModule } from 'react-native'; -import type { CodegenTypes } from 'react-native'; +import { TurboModuleRegistry } from 'react-native'; +import type { TurboModule, CodegenTypes } from 'react-native'; export interface Spec extends TurboModule { isNavigationBarActive(): boolean; getNavigationBarHeight(): CodegenTypes.Double; } -export default TurboModuleRegistry.getEnforcing('MxNavigation'); +let cachedModule: Spec | undefined; +const getModule = (): Spec => { + if (!cachedModule) { + cachedModule = TurboModuleRegistry.getEnforcing('MxNavigation'); + } + return cachedModule; +}; + +// Resolves the native module lazily (on first property access) instead of at import time, +// so requiring this file on web, where TurboModuleRegistry is stubbed out, never crashes. +const NativeMxNavigation = new Proxy({} as Spec, { + get(_target, prop) { + return getModule()[prop as keyof Spec]; + }, +}); + +export default NativeMxNavigation; diff --git a/src/navigation-mode/index.ts b/src/navigation-mode/index.ts index 04209dd..b138213 100644 --- a/src/navigation-mode/index.ts +++ b/src/navigation-mode/index.ts @@ -1,6 +1,12 @@ import NativeMxNavigation from './NativeMxNavigation'; +// Getters keep this lazy: reading .height/.isActive is what triggers the native call, +// not importing this module. export const AndroidNavigationBar = { - height: NativeMxNavigation.getNavigationBarHeight(), - isActive: NativeMxNavigation.isNavigationBarActive(), + get height() { + return NativeMxNavigation.getNavigationBarHeight(); + }, + get isActive() { + return NativeMxNavigation.isNavigationBarActive(); + }, };