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
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@
"@types/react-dom": "^18.3.1",
"react": "18.3.1",
"react-dom": "18.3.1",
"@factorialco/f0-react-native>react": "19.1.0",
"@factorialco/f0-react-native>react-dom": "19.1.0",
"@factorialco/f0-react-native>react-test-renderer": "19.1.0"
"@factorialco/f0-react-native>react": "19.2.3",
"@factorialco/f0-react-native>react-dom": "19.2.3",
"@factorialco/f0-react-native>react-test-renderer": "19.2.3"
}
}
}
48 changes: 33 additions & 15 deletions packages/react-native/babel.config.cjs
Original file line number Diff line number Diff line change
@@ -1,17 +1,35 @@
module.exports = {
presets: [
// Disable the Reanimated/Worklets Babel plugin during the library build.
// The host app's Metro pipeline will workletize callbacks at bundle time
// using its own react-native-worklets version (same pattern as gesture-handler).
["babel-preset-expo", { reanimated: false }],
[
"@babel/preset-typescript",
{
// Allow parsing of TypeScript 5.0+ syntax including const type parameters
allowNamespaces: true,
allowDeclareFields: true,
},
module.exports = function (api) {
// The published library build (react-native-builder-bob) must NOT run the
// Reanimated/Worklets Babel plugin: consuming host apps workletize callbacks
// at bundle time with their own react-native-worklets (same pattern as
// gesture-handler). The bundled playground, however, IS a host app and must
// workletize its own Reanimated worklets — otherwise Reanimated 4 throws
// "[Worklets] Failed to create a worklet" at runtime.
//
// So we add react-native-worklets/plugin only when the caller is Metro (i.e.
// when bundling the playground app), and keep it off for the bob library
// build and for Jest (which mocks Reanimated).
const isAppBundle = api.caller(
(caller) => !!caller && (caller.name === "metro" || !!caller.platform),
);

api.cache.using(() => isAppBundle);

return {
presets: [
// Disable babel-preset-expo's automatic Reanimated/Worklets plugin; we add
// react-native-worklets/plugin explicitly below for app bundles only.
["babel-preset-expo", { reanimated: false }],
[
"@babel/preset-typescript",
{
// Allow parsing of TypeScript 5.0+ syntax including const type parameters
allowNamespaces: true,
allowDeclareFields: true,
},
],
],
],
plugins: [],
// react-native-worklets/plugin must be last. Only enabled for app bundles.
plugins: isAppBundle ? ["react-native-worklets/plugin"] : [],
};
};
2 changes: 1 addition & 1 deletion packages/react-native/jest.config.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export default {
preset: "react-native",
preset: "@react-native/jest-preset",
setupFiles: ["<rootDir>/jest.setup.js"],
transform: {
// Use custom transformer for ViewConfigIgnore.js - match any path containing ViewConfigIgnore.js
Expand Down
23 changes: 23 additions & 0 deletions packages/react-native/jest.setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,29 @@ jest.mock("react-native/Libraries/NativeComponent/ViewConfigIgnore", () => {
};
});

// Mock react-native-worklets to avoid native worklets initialization in Jest.
// Reanimated 4's mock pulls in react-native-worklets, whose native module
// throws "Native part of Worklets doesn't seem to be initialized" outside a
// native runtime.
jest.mock("react-native-worklets", () => ({
createWorkletRuntime: jest.fn(),
createSerializable: jest.fn((v) => v),
isWorkletFunction: jest.fn(() => true),
runOnRuntime: jest.fn(),
runOnJS: jest.fn((fn) => fn),
runOnUI: jest.fn((fn) => fn),
scheduleOnRN: jest.fn(),
scheduleOnUI: jest.fn(),
makeShareable: jest.fn((v) => v),
createSharedValue: jest.fn((v) => ({ value: v })),
RuntimeKind: {
ReactNative: "ReactNative",
UI: "UI",
Custom: "Custom",
},
serializableMappingCache: new WeakMap(),
}));

// Mock react-native-reanimated
jest.mock("react-native-reanimated", () =>
require("react-native-reanimated/mock"),
Expand Down
64 changes: 33 additions & 31 deletions packages/react-native/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,9 @@
"license": "MIT",
"peerDependencies": {
"date-fns": "^3.6.0",
"expo-image": "~3.0.11",
"expo-blur": "~15.0.8",
"expo-image": "*",
"expo-blur": "*",
"expo-clipboard": "*",
"react": "*",
"react-native": "*",
"react-native-reanimated": ">=4.1.1 <5.0.0",
Expand All @@ -105,66 +106,67 @@
"tailwind-merge": "^3.4.0",
"tailwind-variants": "^3.2.2",
"tailwindcss": "^4.1.18",
"uniwind": "^1.2.7",
"uniwind": "^1.9.0",
"twemoji-parser": "^14.0.0"
},
"dependencies": {
"expo-clipboard": "~8.0.8",
"tailwind-merge": "^3.4.0",
"tailwind-variants": "^3.2.2"
},
"devDependencies": {
"@babel/core": "^7.28.0",
"@babel/preset-typescript": "^7.28.5",
"@expo/vector-icons": "^15.0.3",
"@expo/vector-icons": "^15.1.1",
"@react-native/jest-preset": "0.85.3",
"@react-navigation/bottom-tabs": "^7.4.0",
"@react-navigation/elements": "^2.6.3",
"@react-navigation/native": "^7.1.8",
"@svgr/cli": "^8.1.0",
"@testing-library/react-native": "^13.3.3",
"@types/jest": "^30.0.0",
"@types/lodash": "^4.17.16",
"@types/react": "~19.1.0",
"@types/react": "~19.2.0",
"@types/react-test-renderer": "^19.1.0",
"@types/twemoji-parser": "^13.1.4",
"babel-jest": "^30.2.0",
"babel-preset-expo": "^54.0.10",
"babel-preset-expo": "^56.0.15",
"eslint-plugin-react-refresh": "^0.4.26",
"expo": "~54.0.32",
"expo-blur": "~15.0.8",
"expo-clipboard": "^8.0.8",
"expo-constants": "~18.0.13",
"expo-font": "~14.0.11",
"expo-image": "~3.0.11",
"expo-router": "~6.0.22",
"expo-splash-screen": "~31.0.13",
"expo-status-bar": "~3.0.9",
"expo-symbols": "~1.0.8",
"expo-system-ui": "~6.0.9",
"expo-updates": "^29.0.16",
"expo-web-browser": "~15.0.10",
"expo": "~56.0.12",
"expo-blur": "~56.0.3",
"expo-clipboard": "~56.0.4",
"expo-constants": "~56.0.18",
"expo-font": "~56.0.7",
"expo-image": "~56.0.11",
"expo-router": "~56.2.11",
"expo-splash-screen": "~56.0.10",
"expo-status-bar": "~56.0.4",
"expo-symbols": "~56.0.6",
"expo-system-ui": "~56.0.5",
"expo-updates": "~56.0.19",
"expo-web-browser": "~56.0.5",
"glob": "^11.0.0",
"jest": "^30.2.0",
"npm-run-all2": "^8.0.4",
"oxfmt": "^0.26.0",
"oxlint": "^1.33.0",
"react": "19.1.0",
"react-dom": "19.1.0",
"react-native": "0.81.5",
"react": "19.2.3",
"react-dom": "19.2.3",
"react-native": "0.85.3",
"react-native-builder-bob": "^0.40.12",
"react-native-gesture-handler": "~2.28.0",
"react-native-reanimated": "~4.1.7",
"react-native-safe-area-context": "~5.6.0",
"react-native-screens": "~4.16.0",
"react-native-svg": "^15.15.1",
"react-native-gesture-handler": "~2.31.1",
"react-native-reanimated": "~4.3.1",
"react-native-safe-area-context": "~5.7.0",
"react-native-screens": "~4.25.2",
"react-native-svg": "^15.15.4",
"react-native-web": "~0.21.0",
"react-native-worklets": "0.5.1",
"react-test-renderer": "^19.1.0",
"react-native-worklets": "0.8.3",
"react-test-renderer": "^19.2.3",
"tailwind-merge": "^3.4.0",
"tailwind-variants": "^3.2.2",
"tailwindcss": "^4.1.18",
"tailwindcss-animate": "^1.0.7",
"typescript": "~5.9.2",
"uniwind": "^1.2.7"
"uniwind": "^1.9.0"
},
"react-native-builder-bob": {
"source": "src",
Expand Down
Loading
Loading