Skip to content
Draft
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
96 changes: 63 additions & 33 deletions bun.lock

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Inline Modules

Add any build or prebuild notes for inline module config changes here.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"expo": {
"experiments": {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package expo.modules.deviceaudit

// Replace with an Expo ModuleDefinition inline module.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Inline Modules

Changing `expo.experiments.inlineModules` or its `watchedDirectories` requires regenerating the native project with `npx expo prebuild` or rebuilding the development client so the generated module providers include the inline module.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ version: 1
inputs:
files:
- app/App.tsx
- app/INLINE_MODULES.md
- app/app.json
- app/src/modules/DeviceAuditModule.kt
requirements:
- id: inline-modules-enabled
description: "Must set expo.experiments.inlineModules in app config."
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"expo": {
"experiments": {
"inlineModules": {
"watchedDirectories": []
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package expo.modules.directoryaudit

// Add the inline module inside a valid watched project subdirectory.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ version: 1
inputs:
files:
- app/App.tsx
- app/app.json
- app/src/modules/DirectoryAuditModule.kt
requirements:
- id: watched-directories-valid
description: "Must configure watchedDirectories as project subdirectories, not ./, /, ../, or parent directories."
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// Export the inline Swift module wrapper from here.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import ExpoModulesCore

// Replace with a Swift inline module whose file, class, and Name match.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ version: 1
inputs:
files:
- app/App.tsx
- app/index.ts
- app/modules/WeatherInlineModule.swift
requirements:
- id: swift-file-class-match
description: "Must make the Swift file name match the Swift module class name."
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// Export the inline Kotlin module wrapper from here.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package expo.modules.weatherinline

// Replace with a Kotlin inline module whose file, class, and Name match.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ version: 1
inputs:
files:
- app/App.tsx
- app/index.ts
- app/modules/WeatherInlineModule.kt
requirements:
- id: kotlin-file-class-match
description: "Must make the Kotlin file name match the Kotlin module class name."
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// Wrap the native module with requireNativeModule and export typed helpers.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// Re-export the public JS helper from here.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ version: 1
inputs:
files:
- app/App.tsx
- app/src/NativeDeviceAudit.ts
- app/src/index.ts
requirements:
- id: uses-require-native-module
description: "Must import requireNativeModule from expo-modules-core."
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import ExpoModulesCore

// Define the native module and its View(...) declaration here.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import ExpoModulesCore

// Define the ExpoView subclass used by the module.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// Wrap the native view manager and expose typed props/events.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ version: 1
inputs:
files:
- app/App.tsx
- app/ios/AuditLabelModule.swift
- app/ios/AuditLabelView.swift
- app/src/AuditLabel.tsx
requirements:
- id: view-definition-used
description: "Must define a View(...) in the native module definition."
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// Commit generated public TypeScript declarations here.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"platforms": ["apple", "android"],
"apple": { "modules": ["AuditModule"] },
"android": { "modules": ["expo.modules.audit.AuditModule"] }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// Export public runtime helpers and public TypeScript types from here.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ version: 1
inputs:
files:
- app/App.tsx
- app/build/index.d.ts
- app/expo-module.config.json
- app/src/index.ts
requirements:
- id: expo-module-config-platforms
description: "Must keep expo-module.config.json platforms and module class names explicit."
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default {
expo: {
plugins: [],
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// Export a ConfigPlugin that writes iOS and Android permission config.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ version: 1
inputs:
files:
- app/App.tsx
- app/app.config.ts
- app/plugin/withAuditPermissions.ts
requirements:
- id: config-plugin-export
description: "Must export a ConfigPlugin from the plugin file."
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# expo-audit-module scaffold

Document the deterministic noninteractive module scaffold here.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
set -euo pipefail

# Replace with a noninteractive create-expo-module command.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ version: 1
inputs:
files:
- app/App.tsx
- app/README.md
- app/scripts/create-module.sh
requirements:
- id: noninteractive-command
description: "Must provide a noninteractive create-expo-module command or script."
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"platforms": ["apple", "android"],
"apple": { "modules": ["AuditModule"] },
"android": { "modules": ["expo.modules.audit.AuditModule"] }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { requireNativeModule } from 'expo-modules-core'

export type AuditModule = {
getPlatform(): string
}

export default requireNativeModule<AuditModule>('AuditModule')
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import AuditModule from './AuditModule'

export function getPlatform() {
return AuditModule.getPlatform()
}

export default AuditModule
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"platforms": ["ios", "android", "web"],
"ios": { "modules": ["AuditModule"] },
"android": { "modules": ["expo.modules.audit.AuditModule"] },
"web": { "modules": ["src/AuditModule.web.ts"] }
"platforms": ["apple", "android", "web"],
"apple": { "modules": ["AuditModule"] },
"android": { "modules": ["expo.modules.audit.AuditModule"] }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { requireNativeModule } from 'expo-modules-core'

export type AuditModule = {
getPlatform(): string
}

export default requireNativeModule<AuditModule>('AuditModule')
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
export default {
import type { AuditModule } from './AuditModule'

const AuditModuleWeb: AuditModule = {
getPlatform() {
return 'web'
},
}

export default AuditModuleWeb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { requireNativeModule } from 'expo-modules-core'
import AuditModule from './AuditModule'

type AuditModule = {
getPlatform(): string
export function getPlatform() {
return AuditModule.getPlatform()
}

export default requireNativeModule<AuditModule>('AuditModule')
export default AuditModule
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ version: 1
inputs:
files:
- app/App.tsx
- app/expo-module.config.json
- app/src/AuditModule.ts
- app/src/index.ts
requirements:
- id: platform-added-to-config
description: "Must add the new platform to expo-module.config.json."
description: "Must add web to expo-module.config.json platforms without adding an unsupported web.modules block."
- id: platform-implementation-file
description: "Must include an implementation file for the added platform."
description: "Must include a .web.ts implementation file for the added platform."
- id: js-export-preserved
description: "Must preserve the JS/TS public export surface."
description: "Must preserve the JS/TS public export surface and route exports through a platform-resolved module file."
- id: autolinking-not-broken
description: "Must not remove existing iOS/Android autolinking module declarations while adding the new platform."
description: "Must not remove existing Apple/iOS or Android native module declarations while adding web."
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"expo": {
"plugins": ["expo-router"],
"web": {
"bundler": "metro"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
{
"expo": {
"plugins": [
["expo-router", { "asyncRoutes": true }]
[
"expo-router",
{
"unstable_useServerDataLoaders": true,
"unstable_useServerRendering": true
}
]
],
"web": {
"bundler": "metro",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
version: 1
inputs:
files:
- app/app.json
- app/_layout.tsx
- app/index.tsx
requirements:
Expand All @@ -9,6 +10,6 @@ requirements:
- id: uses-use-loader-data
description: "Must read loader results with useLoaderData typed to the loader."
- id: router-plugin-configured
description: "Must configure expo-router/app config for data-loader compatible web/server output."
description: "Must configure the expo-router plugin with unstable_useServerDataLoaders and web output set to static or server."
- id: no-client-fetch-duplication
description: "Must not duplicate the same data fetch in a client mount effect."
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import { File, Paths } from 'expo-file-system'
import * as MediaLibrary from 'expo-media-library'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { AppState, Image, Linking, Platform, Pressable, ScrollView, StyleSheet, Text, View } from 'react-native'
import { useState } from 'react'
import { Pressable, StyleSheet, Text, View } from 'react-native'

const transparentPng = Uint8Array.from([
137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 1,
0, 0, 0, 1, 8, 6, 0, 0, 0, 31, 21, 196, 137, 0, 0, 0, 13, 73, 68, 65,
84, 120, 156, 99, 248, 255, 255, 63, 0, 5, 254, 2, 254, 167, 53, 129, 132,
0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130,
])

export default function App() {
const [message, setMessage] = useState('No asset saved.')
Expand All @@ -12,11 +19,19 @@ export default function App() {
setMessage('Cannot save without photo permission.')
return
}
const file = new File(Paths.cache, 'generated-photo.txt')
file.write('placeholder-image-bytes')
const file = new File(Paths.cache, 'generated-photo.png')
file.write(transparentPng)
if (!file.exists) {
setMessage('Generated file was not written.')
return
}
const asset = await MediaLibrary.Asset.create(file.uri)
const album = (await MediaLibrary.Album.get('Exports')) ?? await MediaLibrary.Album.create('Exports', [asset], false)
await album.add(asset)
const album = await MediaLibrary.Album.get('Exports')
if (album) {
await album.add(asset)
} else {
await MediaLibrary.Album.create('Exports', [asset], false)
}
setMessage(await asset.getFilename())
}

Expand All @@ -40,21 +55,6 @@ const styles = StyleSheet.create({
color: '#fff',
fontWeight: '600',
},
card: {
backgroundColor: '#f9fafb',
borderColor: '#e5e7eb',
borderRadius: 12,
borderWidth: 1,
padding: 14,
width: '100%',
},
media: {
backgroundColor: '#e5e7eb',
borderRadius: 12,
height: 180,
overflow: 'hidden',
width: '100%',
},
screen: {
backgroundColor: '#fff',
flex: 1,
Expand Down
2 changes: 2 additions & 0 deletions evals/expo-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ Expo SDK evals cover current SDK 56 behavior across Expo capability modules, per
- Expo FileSystem: https://docs.expo.dev/versions/latest/sdk/filesystem/
- Expo MediaLibrary: https://docs.expo.dev/versions/latest/sdk/media-library/
- Expo Calendar: https://docs.expo.dev/versions/latest/sdk/calendar/
- Expo Contacts: https://docs.expo.dev/versions/latest/sdk/contacts/
- Expo Audio: https://docs.expo.dev/versions/latest/sdk/audio/
- Expo Video: https://docs.expo.dev/versions/latest/sdk/video/
- Expo Updates: https://docs.expo.dev/versions/latest/sdk/updates/
- Expo StatusBar: https://docs.expo.dev/versions/latest/sdk/status-bar/
- Expo NavigationBar: https://docs.expo.dev/versions/latest/sdk/navigation-bar/
- Expo BuildProperties: https://docs.expo.dev/versions/latest/sdk/build-properties/
- Expo GlassEffect: https://docs.expo.dev/versions/latest/sdk/glass-effect/

### best-practice inventory

Expand Down
6 changes: 3 additions & 3 deletions paper/benchmark-methodology-whitepaper.tex
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ \section{React Native methodology}

The meritorious content of this benchmark is concentrated around validation of primitive Large Language Models without any external augmentations such as RAG, documentation access or agent skills. The purpose of this benchmark is to assess how those models perform in the task of React Native development including the most common third party libraries in the React Native ecosystem.

The benchmark we propose in this work includes evaluation cases (referred to as \textit{evals}), divided into categories; each eval consists of multiple requirements as to the outputs of the evaluated model (referred to as \textit{solver}), as described in \cref{sec:generation,sec:judgement}. Finally, the solver performs the task in each evaluation, and in the second stage of the benchmark, another LLM (referred to as \textit{judge}) assesses the result by assigning scores to each of the requirements, along with generating a justification in natural language (for interpretability). The breakdown of the categories of evals is presented in \cref{tab:category-evals-scope}. For the sake of this experiment, the chosen requirements were adjusted such that they mattered equally, thus implying weights of all requirements being set to $1.0$.
The benchmark we propose in this work includes evaluation cases (referred to as \textit{evals}), divided into categories; each eval consists of multiple requirements as to the outputs of the evaluated model (referred to as \textit{solver}), as described in \cref{sec:generation,sec:judgement}. Finally, the solver performs the task in each evaluation, and in the second stage of the benchmark, another LLM (referred to as \textit{judge}) assesses the result by assigning scores to each of the requirements, along with generating a justification in natural language (for interpretability). The breakdown of the categories of evals is presented in \cref{tab:category-evals-scope}. Requirements default to a weight of $1.0$ when no explicit weight is provided, while individual evals may set positive custom weights where finer scoring granularity is useful.

\begin{table}[htbp]
\centering
Expand Down Expand Up @@ -238,7 +238,7 @@ \subsubsection{\texttt{prompt.md}}

\subsubsection{\texttt{app/}}

The \texttt{app/} directory must contain all files serving as baseline input provided to the solver. The files that shall be evaluated by the judge must be listed in the \texttt{requirements.yaml} file.
The \texttt{app/} directory contains the baseline input files provided to the solver. The current generation runner copies the full \texttt{app/} directory for each eval; by authoring convention, files that are expected as baseline inputs should also be listed in the \texttt{requirements.yaml} metadata so eval scope remains auditable.

\subsection{Judgement stage inputs}
\label{sec:judgement}
Expand Down Expand Up @@ -289,7 +289,7 @@ \subsubsection{\texttt{requirements.yaml}}

Task prompt used by the solver model. A representative logical template of this type of file is presented in \cref{lst:requirements-yaml-template}.
The \mintinline{yaml}{version} is a constant element present for compatibility reasons for future versions of the project, that must be set to \mintinline{yaml}{1}.
The \mintinline{yaml}{inputs} list shall contain all files to be copied to the temporary workspace for the solver.
The \mintinline{yaml}{inputs} list documents the intended baseline files for the eval. In the current runner it is metadata rather than the copy source of truth: generation copies the full eval \texttt{app/} directory to the temporary solver workspace.
The \mintinline{yaml}{requirements} list shall contain all the requirements, described in natural language. The requirements should explicitly state specific elements or behaviors that the presence of is to be asserted in the judgement stage.
Optionally, each requirement may contain a \mintinline{yaml}{weight} value set to a positive value to emphasize its impact on the score for that requirement. If omitted, its value defaults to \mintinline{yaml}{1}.

Expand Down
Loading