Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/bare-expo/android/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ gen-external-apklibs
# Java class files

# Generated files
app/src/main/java/local/

# Gradle files
.gradle
Expand Down
27 changes: 27 additions & 0 deletions apps/bare-expo/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,15 @@ android {
androidResources {
ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~'
}
if (project.properties["expo.localModules.enabled"] == "true") {
sourceSets {
main {
java {
srcDirs += [file('build/local/modules/').absolutePath]
}
}
}
}
}

// Apply static values from `gradle.properties` to the `android.packagingOptions`
Expand Down Expand Up @@ -205,3 +214,21 @@ class ExecuteSetupAndroidProject implements Plugin<Project> {
}

apply plugin: ExecuteSetupAndroidProject

gradle.projectsEvaluated {
if (project.properties["expo.localModules.enabled"] == "true") {
providers.exec {
workingDir(projectRoot)
commandLine(
"node",
"--no-warnings",
"--eval",
"require(require.resolve('expo-modules-autolinking', { paths: [require.resolve('expo/package.json')] }))(process.argv.slice(1))",
"mirror-kotlin-local-modules",
file('src/main/java/local/modules/').absolutePath,
file('build/local/modules/').absolutePath,
project.properties["expo.localModules.watchedDirs"]
)
}.standardOutput.asText.get()
}
}
3 changes: 3 additions & 0 deletions apps/bare-expo/android/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,6 @@ expo.useLegacyPackaging=false
EXPO_ALLOW_GLIDE_LOGS=true

android.enableMinifyInReleaseBuilds=true

expo.localModules.enabled=true
expo.localModules.watchedDirs=["../native-component-list/src/screens/LocalModules/localModulesExamples"]
8 changes: 7 additions & 1 deletion apps/bare-expo/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@
],
"./plugins/withAndroidNetworkSecurityConfig",
"./plugins/withBenchmarkModules"
]
],
"experiments": {
"localModules": true
},
"localModules": {
"watchedDirs": ["../native-component-list/src/screens/LocalModules/localModulesExamples"]
}
}
}
8 changes: 8 additions & 0 deletions apps/bare-expo/ios/BareExpo.xcodeproj/project.pbxproj

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

296 changes: 148 additions & 148 deletions apps/bare-expo/ios/Podfile.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions apps/bare-expo/ios/Podfile.properties.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@
"expo.jsEngine": "hermes",
"newArchEnabled": "true",
"EX_DEV_CLIENT_NETWORK_INSPECTOR": "true",
"expo.localModules.enabled": "true",
"expo.localModules.watchedDirs": "[\"../native-component-list/src/screens/LocalModules/localModulesExamples\"]",
"ios.buildReactNativeFromSource": "false"
}
52 changes: 51 additions & 1 deletion apps/bare-expo/metro.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-env node */
// Learn more https://docs.expo.dev/guides/customizing-metro/
const { getDefaultConfig } = require('expo/metro-config');
const fs = require('node:fs');
const path = require('node:path');

/** @type {import('expo/metro-config').MetroConfig} */
Expand All @@ -24,6 +25,26 @@ config.watchFolders = [
path.join(monorepoRoot, 'apps/test-suite'), // Workaround for Yarn v1 workspace issue where workspace dependencies aren't properly linked, should be at `<root>/node_modules/apps/test-suite`
];

function findUpTSConfig(cwd) {
const tsconfigPath = path.resolve(cwd, './tsconfig.json');
if (fs.existsSync(tsconfigPath)) {
return path.dirname(tsconfigPath);
}

const parent = path.dirname(cwd);
if (parent === cwd) return null;

return findUpTSConfig(parent);
}

function findUpTSProjectRootOrAssert(dir) {
const tsProjectRoot = findUpTSConfig(dir);
if (!tsProjectRoot) {
throw new Error('Local modules watched dir needs to be inside a TS project with tsconfig.json');
}
return tsProjectRoot;
}

// When testing on MacOS we need to swap out `react-native` for `react-native-macos`
config.resolver.resolveRequest = (context, moduleName, platform) => {
if (
Expand All @@ -33,7 +54,36 @@ config.resolver.resolveRequest = (context, moduleName, platform) => {
const newModuleName = moduleName.replace('react-native', 'react-native-macos');
return context.resolveRequest(context, newModuleName, platform);
}
return context.resolveRequest(context, moduleName, platform);

const localModulesModulesPath = path.resolve(__dirname, './.expo/localModules/modules');

let localModuleFileExtension = null;
if (moduleName.endsWith('.module')) {
localModuleFileExtension = '.module.js';
} else if (moduleName.endsWith('.view')) {
localModuleFileExtension = '.view.js';
}
if (localModuleFileExtension) {
const tsProjectRoot = findUpTSProjectRootOrAssert(path.dirname(context.originModulePath));
const relativePathToOriginModule = path.relative(
tsProjectRoot,
fs.realpathSync(path.dirname(context.originModulePath))
);

const modulePath = path.resolve(
localModulesModulesPath,
relativePathToOriginModule,
moduleName.substring(0, moduleName.lastIndexOf('.')) + localModuleFileExtension
);

return {
filePath: modulePath,
type: 'sourceFile',
};
}

const resolution = context.resolveRequest(context, moduleName, platform);
return resolution;
};

// When testing on MacOS we need to include the `react-native-macos/Libraries/Core/InitializeCore` as prepended global module
Expand Down
6 changes: 6 additions & 0 deletions apps/native-component-list/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@
"eas": {
"projectId": "2c28de10-a2cd-11e6-b8ce-59d1587e6774"
}
},
"experiments": {
"localModules": true
},
"localModules": {
"watchedDirs": ["src/screens/LocalModules/localModulesExamples/"]
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@ export const ScreensList: ScreenConfig[] = [
},
name: 'Battery',
},
{
getComponent() {
return optionalRequire(() => require('../screens/LocalModules/LocalModulesScreen'));
},
name: 'LocalModules',
},
{
getComponent() {
return optionalRequire(() => require('../screens/Blob/BlobScreen'));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Text } from 'react-native';

import SimpleModule from './localModulesExamples/SimpleModule.module';
import TestView from './localModulesExamples/TestView.view';

export default function LocalModulesScreen() {
return (
<>
<Text>{SimpleModule.test}</Text>
<TestView style={{ flex: 1 }} url="https://docs.expo.dev/modules/" />
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package localModulesExamples

import expo.modules.kotlin.modules.Module
import expo.modules.kotlin.modules.ModuleDefinition

public class SimpleModule : Module() {
override fun definition() = ModuleDefinition {
Constant("test") { ->
"Kotlin constant 1 new localModulesProvider"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import ExpoModulesCore
import WebKit

public class SimpleModule: Module {
public func definition() -> ModuleDefinition {
Constant("test") {
return "Swift constant 1283"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@

package localModulesExamples

import expo.modules.kotlin.modules.Module
import expo.modules.kotlin.modules.ModuleDefinition
import java.net.URL

import android.content.Context
import android.webkit.WebView
import android.webkit.WebViewClient
import expo.modules.kotlin.AppContext
import expo.modules.kotlin.viewevent.EventDispatcher
import expo.modules.kotlin.views.ExpoView

class TestView : Module() {
override fun definition() = ModuleDefinition {
View(ExpoWebView::class) {
Events("onLoad")

Prop("url") { view: ExpoWebView, url: URL? ->
view.webView.loadUrl(url.toString())
}
}
}
}

class ExpoWebView(context: Context, appContext: AppContext) : ExpoView(context, appContext) {
private val onLoad by EventDispatcher()

internal val webView = WebView(context).also {
it.layoutParams = LayoutParams(
LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT
)

it.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView, url: String) {
onLoad(mapOf("url" to url))
}
}

addView(it)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import ExpoModulesCore
import WebKit

public class TestView: Module {
public func definition() -> ModuleDefinition {
View(ExpoWebView.self) {
Events("onLoad")

Prop("url") { (view, url: URL) in
if view.webView.url != url {
let urlRequest = URLRequest(url: url)
view.webView.load(urlRequest)
}
}
}
}
}

class ExpoWebView: ExpoView, WKNavigationDelegate {
let webView = WKWebView()
let onLoad = EventDispatcher()

required init(appContext: AppContext? = nil) {
super.init(appContext: appContext)
clipsToBounds = true
webView.navigationDelegate = self
addSubview(webView)
}

override func layoutSubviews() {
webView.frame = bounds
}

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
if let url = webView.url {
onLoad([
"url": url.absoluteString
])
}
}
}
10 changes: 8 additions & 2 deletions apps/native-component-list/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@
"strictPropertyInitialization": true,
"strictFunctionTypes": true,
"noImplicitThis": true,
"noImplicitReturns": true
"noImplicitReturns": true,
"rootDirs": ["./", "../bare-expo/.expo/localModules/types"]
},
"include": ["App.tsx", "src/**/*", "ts-declarations/**/*"]
"include": [
"App.tsx",
"src/**/*",
"ts-declarations/**/*",
"../bare-expo/.expo/localModules/types/**/*"
]
}
2 changes: 2 additions & 0 deletions docs/constants/navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ export const general = [
makeSection('Tutorials', [
makePage('modules/native-module-tutorial.mdx'),
makePage('modules/native-view-tutorial.mdx'),
makePage('modules/local-native-modules-tutorial.mdx'),
makePage('modules/config-plugin-and-native-module-tutorial.mdx'),
makePage('modules/use-standalone-expo-module-in-your-project.mdx'),
makePage('modules/third-party-library.mdx'),
Expand All @@ -313,6 +314,7 @@ export const general = [
]),
makeSection('Reference', [
makePage('modules/module-api.mdx'),
makePage('modules/local-native-modules-reference.mdx'),
makePage('modules/android-lifecycle-listeners.mdx'),
makePage('modules/appdelegate-subscribers.mdx'),
makePage('modules/autolinking.mdx'),
Expand Down
Loading
Loading