Merge pull request #3 from castdrian/build-workflow

feat: build pipelines & nx-expo app link
This commit is contained in:
Jorrin
2024-01-15 13:39:28 +01:00
committed by GitHub
35 changed files with 7574 additions and 6830 deletions

143
.github/workflows/build-mobile.yml vendored Normal file
View File

@@ -0,0 +1,143 @@
name: build mobile app
on:
push:
branches:
- master
workflow_dispatch:
permissions:
contents: write
jobs:
build-android:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 21
- uses: pnpm/action-setup@v2
name: Install pnpm
with:
version: 8
run_install: false
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Setup Android SDK
uses: android-actions/setup-android@v3
- name: Install dependencies
run: pnpm install
- name: Build native Android modules
run: pnpm exec nx run mobile:prebuild --platform=android
- name: Build Android app
run: cd apps/mobile/android && ./gradlew assembleRelease
- name: Rename apk
run: mv apps/mobile/android/app/build/outputs/apk/release/app-release.apk apps/mobile/android/app/build/outputs/apk/release/movie-web.apk
- name: Upload movie-web.apk as artifact
uses: actions/upload-artifact@v4
with:
name: apk
path: ./apps/mobile/android/app/build/outputs/apk/release/movie-web.apk
build-ios:
runs-on: macos-13
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Xcode Select Version
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '15.1.0'
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 21
- uses: pnpm/action-setup@v2
name: Install pnpm
with:
version: 8
run_install: false
- name: Install dependencies
run: pnpm install
- name: Build native iOS modules
run: pnpm exec nx run mobile:prebuild --platform=ios
- name: Build iOS app
run: cd apps/mobile/ios && xcodebuild archive -workspace movieweb.xcworkspace -scheme "movieweb" -sdk iphoneos -configuration "Release" -archivePath "build/app.xcarchive" -destination generic/platform=iOS CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
- name: Export .ipa from .xcarchive
run: |
mv apps/mobile/ios/build/app.xcarchive/Products/Applications apps/mobile/ios/build/app.xcarchive/Products/Payload
cd apps/mobile/ios/build/app.xcarchive/Products
zip -r ../../movie-web.ipa Payload
- name: Upload movie-web.ipa as artifact
uses: actions/upload-artifact@v4
with:
name: ipa
path: ./apps/mobile/ios/build/movie-web.ipa
release-app:
runs-on: ubuntu-latest
needs: [build-android, build-ios]
if: github.ref == 'refs/heads/master'
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Automated Version Bump
uses: phips28/gh-action-bump-version@v10.1.1
with:
skip-tag: 'true'
commit-message: 'chore: bump version to {{version}} [skip ci]'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PACKAGEJSON_DIR: 'apps/mobile'
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
merge-multiple: true
- name: Get package version
id: package-version
uses: martinbeentjes/npm-get-version-action@v1.3.1
with:
path: apps/mobile
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: v${{ steps.package-version.outputs.current-version }}
files: |
movie-web.apk
movie-web.ipa
fail_on_unmatched_files: true
token: ${{ env.GITHUB_TOKEN }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

18
.gitignore vendored
View File

@@ -39,3 +39,21 @@ testem.log
Thumbs.db
.nx/cache
# Expo
node_modules/
.expo/
.yarn/
dist/
npm-debug.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision
*.orig.*
web-build/
cache/
apps/mobile-e2e/artifacts

View File

@@ -3,5 +3,6 @@
"dbaeumer.vscode-eslint",
"editorconfig.editorconfig",
"esbenp.prettier-vscode",
"firsttris.vscode-jest-runner"
]
}

View File

@@ -3,31 +3,68 @@
Native-app for movie-web.
## Getting started
```
```bash
pnpm install
// Having nx installed globally is recommended
pnpm install -g nx
// If you don't want nx installed globally, you can use the following command
(pnpm dlx or npx) nx <command>
(pnpm exec or npx) nx <command>
```
## Running tasks
To execute tasks with Nx use the following syntax:
```
```bash
nx <target> <project> <...options>
```
For example, to run the mobile app:
### Android
```bash
nx run mobile:android
```
nx run mobile
### iOS
```bash
nx run mobile:ios
```
## Building archives
### Android .apk
```bash
nx run mobile:prebuild --platform=android
cd apps/mobile/android && ./gradlew assembleRelease
```
### iOS .app
#### Real device
```bash
nx run mobile:prebuild --platform=ios
cd apps/mobile/ios && xcodebuild archive -workspace movieweb.xcworkspace -scheme "movieweb" -sdk iphoneos -configuration "Release" -archivePath "build/app.xcarchive" -destination generic/platform=iOS CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
```
#### Simulator
```bash
nx run mobile:prebuild --platform=ios
cd apps/mobile/ios && xcodebuild archive -workspace movieweb.xcworkspace -scheme "movieweb" -sdk iphonesimulator -configuration "Release" -archivePath "build/app.xcarchive" -destination "generic/platform=iOS Simulator" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
```
## Repository information
This project uses Nx to manage the monorepo. For more information about Nx, visit [nx.dev](https://nx.dev).
### Mobile app
The mobile app is built with React Native and Expo. For more information about Expo, visit [expo.io](https://expo.io).

11
apps/mobile-e2e/.babelrc Normal file
View File

@@ -0,0 +1,11 @@
{
"presets": [
[
"@nx/react/babel",
{
"runtime": "automatic",
},
],
],
"plugins": [],
}

View File

@@ -0,0 +1,89 @@
{
"testRunner": {
"args": {
"$0": "jest",
"config": "./jest.config.json"
},
"jest": {
"setupTimeout": 120000
}
},
"apps": {
"ios.debug": {
"type": "ios.app",
"build": "cd ../../apps/mobile/ios && xcodebuild -workspace movieweb.xcworkspace -scheme movieweb -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
"binaryPath": "../../apps/mobile/ios/build/Build/Products/Debug-iphonesimulator/movieweb.app"
},
"ios.release": {
"type": "ios.app",
"build": "cd ../../apps/mobile/ios && xcodebuild -workspace movieweb.xcworkspace -scheme movieweb -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14' -derivedDataPath ./build -quiet",
"binaryPath": "../../apps/mobile/ios/build/Build/Products/Release-iphonesimulator/movieweb.app"
},
"ios.local": {
"type": "ios.app",
"build": "pnpm exec nx run mobile:build --platform ios --profile preview --wait --local --no-interactive --output=../../apps/mobile/dist/movieweb.tar.gz",
"binaryPath": "../../apps/mobile/dist/movieweb.app"
},
"android.debug": {
"type": "android.apk",
"build": "cd ../../apps/mobile/android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug",
"binaryPath": "../../apps/mobile/android/app/build/outputs/apk/debug/app-debug.apk"
},
"android.release": {
"type": "android.apk",
"build": "cd ../../apps/mobile/android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release",
"binaryPath": "../../apps/mobile/android/app/build/outputs/apk/release/app-release.apk"
},
"android.local": {
"type": "android.apk",
"build": "pnpm exec nx run mobile:build --platform android --profile preview --wait --local --no-interactive --output=../../apps/mobile/dist/movieweb.apk",
"binaryPath": "../../apps/mobile/dist/movieweb.apk"
}
},
"devices": {
"simulator": {
"type": "ios.simulator",
"device": {
"type": "iPhone 14"
}
},
"emulator": {
"type": "android.emulator",
"device": {
"avdName": "Pixel_4a_API_30"
}
}
},
"configurations": {
"ios.sim.release": {
"device": "simulator",
"app": "ios.release"
},
"ios.sim.debug": {
"device": "simulator",
"app": "ios.debug"
},
"ios.sim.local": {
"device": "simulator",
"app": "ios.local"
},
"android.emu.release": {
"device": "emulator",
"app": "android.release"
},
"android.emu.debug": {
"device": "emulator",
"app": "android.debug"
},
"android.emu.local": {
"device": "emulator",
"app": "android.local"
}
}
}

View File

@@ -0,0 +1,18 @@
{
"extends": ["../../.eslintrc.js"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}

View File

@@ -0,0 +1,22 @@
{
"preset": "../../jest.preset",
"rootDir": ".",
"testMatch": [
"<rootDir>/src/**/*.test.ts?(x)",
"<rootDir>/src/**/*.spec.ts?(x)"
],
"testTimeout": 120000,
"maxWorkers": 1,
"globalSetup": "detox/runners/jest/globalSetup",
"globalTeardown": "detox/runners/jest/globalTeardown",
"reporters": ["detox/runners/jest/reporter"],
"testEnvironment": "detox/runners/jest/testEnvironment",
"verbose": true,
"setupFilesAfterEnv": ["<rootDir>/test-setup.ts"],
"transform": {
"^.+\\.(ts|js|html)$": [
"ts-jest",
{ "tsconfig": "<rootDir>/tsconfig.e2e.json" }
]
}
}

View File

@@ -0,0 +1,76 @@
{
"name": "mobile-e2e",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/mobile-e2e/src",
"projectType": "application",
"targets": {
"build-ios": {
"executor": "@nx/detox:build",
"options": {
"detoxConfiguration": "ios.sim.local"
},
"configurations": {
"bare": {
"detoxConfiguration": "ios.sim.debug"
},
"production": {
"detoxConfiguration": "ios.sim.release"
}
}
},
"test-ios": {
"executor": "@nx/detox:test",
"options": {
"detoxConfiguration": "ios.sim.local",
"buildTarget": "mobile-e2e:build-ios"
},
"configurations": {
"bare": {
"detoxConfiguration": "ios.sim.debug",
"buildTarget": "mobile-e2e:build-ios:bare"
},
"production": {
"detoxConfiguration": "ios.sim.release",
"buildTarget": "mobile-e2e:build-ios:production"
}
}
},
"build-android": {
"executor": "@nx/detox:build",
"options": {
"detoxConfiguration": "android.emu.local"
},
"configurations": {
"bare": {
"detoxConfiguration": "android.emu.debug"
},
"production": {
"detoxConfiguration": "android.emu.release"
}
}
},
"test-android": {
"executor": "@nx/detox:test",
"options": {
"detoxConfiguration": "android.emu.local",
"buildTarget": "mobile-e2e:build-android"
},
"configurations": {
"bare": {
"detoxConfiguration": "android.emu.debug",
"buildTarget": "mobile-e2e:build-android:bare"
},
"production": {
"detoxConfiguration": "android.emu.release",
"buildTarget": "mobile-e2e:build-android:production"
}
}
},
"lint": {
"executor": "@nx/eslint:lint",
"outputs": ["{options.outputFile}"]
}
},
"tags": [],
"implicitDependencies": ["mobile"]
}

View File

@@ -0,0 +1,11 @@
import { device, element, by, expect } from 'detox';
describe('movieweb', () => {
beforeEach(async () => {
await device.reloadReactNative();
});
it('should display welcome message', async () => {
await expect(element(by.id('heading'))).toHaveText('Welcome movie-web 👋');
});
});

View File

@@ -0,0 +1,5 @@
import { device } from 'detox';
beforeAll(async () => {
await device.launchApp();
});

View File

@@ -0,0 +1,10 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"sourceMap": false,
"outDir": "../../dist/out-tsc",
"allowJs": true,
"types": ["node", "jest", "detox"]
},
"include": ["src/**/*.ts", "src/**/*.js"]
}

View File

@@ -0,0 +1,10 @@
{
"extends": "../../tsconfig.base.json",
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.e2e.json"
}
]
}

View File

@@ -0,0 +1,18 @@
{
"extends": ["../../.eslintrc.js"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}

View File

@@ -7,6 +7,8 @@ node_modules/
.expo/
dist/
web-build/
android/
ios/
# Native
*.orig.*
@@ -34,6 +36,7 @@ yarn-error.*
# typescript
*.tsbuildinfo
# @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb
# The following patterns were generated by expo-cli

View File

@@ -1,35 +1,53 @@
{
"expo": {
"name": "mobile",
"slug": "mobile",
"name": "movie-web",
"slug": "mw-mobile",
"version": "1.0.0",
"orientation": "portrait",
"scheme":"dev.movieweb.app",
"icon": "./assets/images/icon.png",
"scheme": "myapp",
"userInterfaceStyle": "automatic",
"splash": {
"image": "./assets/images/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"assetBundlePatterns": ["**/*"],
"jsEngine": "jsc",
"updates": {
"fallbackToCacheTimeout": 0
},
"assetBundlePatterns": [
"**/*"
],
"ios": {
"supportsTablet": true
"supportsTablet": true,
"bundleIdentifier": "dev.movieweb.app"
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/images/adaptive-icon.png",
"backgroundColor": "#ffffff"
}
"backgroundColor": "#FFFFFF"
},
"package": "dev.movieweb.app"
},
"web": {
"bundler": "metro",
"output": "static",
"favicon": "./assets/images/favicon.png"
"favicon": "./assets/images/favicon.png",
"bundler": "metro"
},
"plugins": ["expo-router"],
"experiments": {
"typedRoutes": true
}
"plugins": [
"expo-router",
[
"@config-plugins/detox",
{
"skipProguard": false,
"subdomains": [
"10.0.2.2",
"localhost"
]
}
]
],
"experiments": {
"typedRoutes": true
}
}
}

View File

@@ -17,7 +17,7 @@ export {
// eslint-disable-next-line camelcase
export const unstable_settings = {
// Ensure that reloading on `/modal` keeps a back button present.
initialRouteName: '(tabs)',
initialRouteName: '(tabs)/index',
};
// Prevent the splash screen from auto-hiding before asset loading is complete.

View File

@@ -2,9 +2,9 @@ module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: [
// Required for expo-router
'expo-router/babel',
],
plugins: [
// Required for expo-router
'expo-router/babel',
],
};
};

28
apps/mobile/eas.json Normal file
View File

@@ -0,0 +1,28 @@
{
"build": {
"production": {
"android": {
"buildType": "app-bundle"
}
},
"development": {
"developmentClient": true,
"distribution": "internal"
},
"preview": {
"distribution": "internal",
"ios": {
"simulator": true
},
"android": {
"buildType": "apk"
}
}
},
"submit": {
"production": {}
},
"cli": {
"version": ">= 5.2.0"
}
}

View File

@@ -0,0 +1,13 @@
module.exports = {
displayName: 'mobile',
resolver: '@nx/jest/plugins/resolver',
preset: 'jest-expo',
transformIgnorePatterns: [
'node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg)',
],
moduleFileExtensions: ['ts', 'js', 'html', 'tsx', 'jsx'],
setupFilesAfterEnv: ['<rootDir>/test-setup.ts'],
moduleNameMapper: {
'\\.svg$': '@nx/expo/plugins/jest/svg-mock',
},
};

View File

@@ -1,27 +1,36 @@
// Learn more https://docs.expo.io/guides/customizing-metro
const { getDefaultConfig } = require('expo/metro-config');
const path = require('path');
const { withNxMetro } = require('@nx/expo');
const { getDefaultConfig } = require('@expo/metro-config');
const { mergeConfig } = require('metro-config');
const exclusionList = require('metro-config/src/defaults/exclusionList');
const projectRoot = __dirname;
const workspaceRoot = path.resolve(projectRoot, '../..');
const defaultConfig = getDefaultConfig(__dirname);
const { assetExts, sourceExts } = defaultConfig.resolver;
/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(projectRoot, {
// [Web-only]: Enables CSS support in Metro.
isCSSEnabled: true,
/**
* Metro configuration
* https://facebook.github.io/metro/docs/configuration
*
* @type {import('metro-config').MetroConfig}
*/
const customConfig = {
transformer: {
babelTransformerPath: require.resolve('react-native-svg-transformer'),
},
resolver: {
assetExts: assetExts.filter((ext) => ext !== 'svg'),
sourceExts: [...sourceExts, 'svg'],
blockList: exclusionList([/^(?!.*node_modules).*\/dist\/.*/]),
// unstable_enableSymlinks: true,
// unstable_enablePackageExports: true,
},
};
module.exports = withNxMetro(mergeConfig(defaultConfig, customConfig), {
// Change this to true to see debugging info.
// Useful if you have issues resolving modules
debug: false,
// all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx', 'json'
extensions: [],
// Specify folders to watch, in addition to Nx defaults (workspace libraries and node_modules)
watchFolders: [],
});
if (config.resolver) {
// 1. Watch all files within the monorepo
config.watchFolders = [workspaceRoot];
// 2. Let Metro know where to resolve packages and in what order
config.resolver.nodeModulesPaths = [
path.resolve(projectRoot, 'node_modules'),
path.resolve(workspaceRoot, 'node_modules'),
];
// 3. Force Metro to resolve (sub)dependencies only from the `nodeModulesPaths`
// TODO: This is supposed to be true, but I couldn't get it to work in the monorepo.
config.resolver.disableHierarchicalLookup = false;
}
module.exports = config;

View File

@@ -2,54 +2,64 @@
"name": "mobile",
"main": "expo-router/entry",
"version": "1.0.0",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web",
"test": "jest --watchAll",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
"format": "prettier --write ."
},
"jest": {
"preset": "jest-expo"
},
"private": true,
"dependencies": {
"@babel/core": "*",
"@expo/metro-config": "*",
"@expo/vector-icons": "^13.0.0",
"@react-navigation/native": "^6.0.2",
"expo": "~49.0.15",
"expo-font": "~11.4.0",
"expo-linking": "~5.0.2",
"expo-router": "^2.0.0",
"@nx/expo": "*",
"@react-navigation/native": "^6.1.9",
"@testing-library/jest-native": "*",
"@testing-library/react-native": "*",
"expo": "*",
"expo-font": "^11.4.0",
"expo-linking": "^6.0.0",
"expo-router": "^2.0.14",
"expo-splash-screen": "~0.20.5",
"expo-status-bar": "~1.6.0",
"expo-system-ui": "~2.4.0",
"expo-web-browser": "~12.3.2",
"react": "18.2.0",
"expo-status-bar": "*",
"expo-system-ui": "^2.6.0",
"expo-web-browser": "^12.5.0",
"metro-config": "*",
"pod-install": "*",
"react": "*",
"react-dom": "18.2.0",
"react-native": "0.72.6",
"react-native-gesture-handler": "~2.12.0",
"react-native-safe-area-context": "4.6.3",
"react-native-screens": "~3.22.0",
"react-native-web": "~0.19.6"
"react-native": "*",
"react-native-gesture-handler": "^2.14.1",
"react-native-safe-area-context": "^4.8.2",
"react-native-screens": "^3.29.0",
"react-native-svg": "*",
"react-native-svg-transformer": "*",
"react-native-web": "^0.19.10"
},
"scripts": {
"eas-build-pre-install": "cd ../../ && node tools/scripts/eas-build-pre-install.mjs . apps/mobile && cp pnpm-lock.yaml apps/mobile",
"eas-build-post-install": "cd ../../ && node tools/scripts/eas-build-post-install.mjs . apps/mobile",
"android": "expo run:android",
"ios": "expo run:ios",
"test": "jest --watchAll",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
"format": "prettier --write ."
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@rnx-kit/metro-config": "^1.3.14",
"@rnx-kit/metro-resolver-symlinks": "^0.1.34",
"@types/react": "~18.2.14",
"@types/react": "18.0.28",
"eslint-plugin-react": "7.32.2",
"eslint-plugin-react-hooks": "4.6.0",
"jest": "^29.2.1",
"jest": "^29.4.1",
"jest-expo": "~49.0.0",
"pod-install": "^0.1.39",
"react-test-renderer": "18.2.0",
"typescript": "^5.1.3"
"typescript": "~5.2.2"
},
"overrides": {
"react-refresh": "~0.14.0"
},
"resolutions": {
"react-refresh": "~0.14.0"
},
"private": true
}
}

94
apps/mobile/project.json Normal file
View File

@@ -0,0 +1,94 @@
{
"name": "mobile",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/mobile/src",
"projectType": "application",
"targets": {
"start": {
"executor": "@nx/expo:start",
"dependsOn": ["ensure-symlink", "sync-deps"],
"options": {
"port": 8081
}
},
"serve": {
"executor": "nx:run-commands",
"options": {
"command": "nx start mobile"
}
},
"run-ios": {
"executor": "@nx/expo:run",
"dependsOn": ["ensure-symlink", "sync-deps"],
"options": {
"platform": "ios"
}
},
"run-android": {
"executor": "@nx/expo:run",
"dependsOn": ["ensure-symlink", "sync-deps"],
"options": {
"platform": "android"
}
},
"build": {
"executor": "@nx/expo:build",
"options": {}
},
"submit": {
"executor": "@nx/expo:submit",
"options": {}
},
"build-list": {
"executor": "@nx/expo:build-list",
"options": {}
},
"sync-deps": {
"executor": "@nx/expo:sync-deps",
"options": {}
},
"ensure-symlink": {
"executor": "@nx/expo:ensure-symlink",
"options": {}
},
"prebuild": {
"executor": "@nx/expo:prebuild",
"dependsOn": ["ensure-symlink", "sync-deps"],
"options": {}
},
"install": {
"executor": "@nx/expo:install",
"options": {}
},
"update": {
"executor": "@nx/expo:update",
"options": {}
},
"export": {
"executor": "@nx/expo:export",
"dependsOn": ["ensure-symlink", "sync-deps"],
"options": {
"platform": "all",
"outputDir": "../../dist/apps/mobile"
}
},
"export-web": {
"executor": "@nx/expo:export",
"options": {
"bundler": "metro"
}
},
"lint": {
"executor": "@nx/eslint:lint",
"outputs": ["{options.outputFile}"]
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "apps/mobile/jest.config.ts"
}
}
},
"tags": []
}

View File

@@ -0,0 +1 @@
import '@testing-library/jest-native/extend-expect';

View File

@@ -0,0 +1,15 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": ["node"]
},
"files": ["../../node_modules/@nx/expo/typings/svg.d.ts"],
"exclude": [
"jest.config.ts",
"**/*.spec.ts",
"**/*.spec.tsx",
"test-setup.ts"
],
"include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"]
}

View File

@@ -1,7 +1,29 @@
{
"extends": ["../../tsconfig.base", "expo/tsconfig.base"],
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"strict": true
"allowSyntheticDefaultImports": true,
"jsx": "react-native",
"lib": [
"dom",
"esnext"
],
"moduleResolution": "node",
"skipLibCheck": true,
"resolveJsonModule": true,
"strict": true,
"declaration": true
},
"include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"]
"files": [],
"include": [
".expo/types/**/*.ts",
"expo-env.d.ts"
],
"references": [
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}

View File

@@ -0,0 +1,20 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node"]
},
"include": [
"jest.config.ts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.test.tsx",
"src/**/*.spec.tsx",
"src/**/*.test.js",
"src/**/*.spec.js",
"src/**/*.test.jsx",
"src/**/*.spec.jsx",
"src/**/*.d.ts"
]
}

3
babel.config.json Normal file
View File

@@ -0,0 +1,3 @@
{
"babelrcRoots": ["*"]
}

5
jest.config.ts Normal file
View File

@@ -0,0 +1,5 @@
import { getJestProjects } from '@nx/jest';
export default {
projects: getJestProjects(),
};

3
jest.preset.js Normal file
View File

@@ -0,0 +1,3 @@
const nxPreset = require('@nx/jest/preset').default;
module.exports = { ...nxPreset };

13
nx.json
View File

@@ -11,6 +11,19 @@
},
"lint": {
"cache": true
},
"@nx/jest:jest": {
"cache": true,
"inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"],
"options": {
"passWithNoTests": true
},
"configurations": {
"ci": {
"ci": true,
"codeCoverage": true
}
}
}
},
"namedInputs": {

View File

@@ -5,12 +5,27 @@
"scripts": {},
"private": true,
"devDependencies": {
"@config-plugins/detox": "~6.0.0",
"@expo/cli": "~0.10.13",
"@nx/detox": "17.2.8",
"@nx/expo": "17.2.8",
"@nx/jest": "17.2.8",
"@nx/js": "17.2.8",
"@nx/workspace": "17.2.8",
"@swc-node/register": "~1.6.8",
"@swc/core": "~1.3.102",
"@testing-library/jest-dom": "5.16.5",
"@testing-library/jest-native": "~5.4.3",
"@testing-library/react-native": "~12.3.0",
"@types/jest": "^29.4.0",
"@types/node": "18.16.9",
"@types/react": "18.0.28",
"@typescript-eslint/eslint-plugin": "^6.18.1",
"@typescript-eslint/parser": "^6.18.1",
"babel-jest": "^29.4.1",
"babel-preset-expo": "~9.5.2",
"detox": "^20.11.1",
"eas-cli": "~5.2.0",
"eslint": "^8.56.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^9.1.0",
@@ -20,8 +35,30 @@
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-react": "7.32.2",
"eslint-plugin-react-hooks": "4.6.0",
"jest": "^29.4.1",
"jest-circus": "^29.4.1",
"jest-environment-jsdom": "^29.4.1",
"jest-expo": "~49.0.0",
"metro": "0.76.8",
"metro-resolver": "0.76.8",
"nx": "17.2.8",
"prettier": "^3.0.3",
"react-test-renderer": "18.2.0",
"ts-jest": "^29.1.0",
"ts-node": "10.9.1",
"typescript": "~5.2.2"
},
"dependencies": {
"@expo/metro-config": "~0.10.7",
"expo": "49.0.16",
"expo-splash-screen": "~0.20.5",
"expo-status-bar": "~1.6.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-native": "0.72.6",
"react-native-svg": "13.9.0",
"react-native-svg-transformer": "1.3.0",
"react-native-web": "~0.19.9",
"tslib": "^2.3.0"
}
}

13422
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,28 @@
/**
* This script is used to patch the '@nx/expo' package to work with EAS Build.
* It is run as a eas-build-post-install script in the 'package.json' of expo app.
* It is executed as 'node tools/scripts/eas-build-post-install.mjs <workspace root> <project root>'
* It will create a symlink from the project's node_modules to the workspace's node_modules.
*/
import { symlink, existsSync } from 'fs';
import { join } from 'path';
const [workspaceRoot, projectRoot] = process.argv.slice(2);
if (existsSync(join(workspaceRoot, 'node_modules'))) {
console.log('Symlink already exists');
process.exit(0);
}
symlink(
join(projectRoot, 'node_modules'),
join(workspaceRoot, 'node_modules'),
'dir',
(err) => {
if (err) console.log(err);
else {
console.log('Symlink created');
}
},
);

View File

@@ -0,0 +1,33 @@
/*
* This script is used to patch the '@nx/expo' package to work with EAS Build.
* It is run as the eas-build-pre-install script in the 'package.json' of expo app.
* It is executed as 'node tools/scripts/eas-build-pre-install.mjs <workspace root> <project root>'
* It will copy the dependencies and devDependencies from the workspace package.json to project's package.json.
* This is needed because EAS Build does the install in project's directory and not workspace's directory.
*/
import { readFileSync, writeFileSync } from 'fs';
import { join } from 'path';
const [workspaceRoot, projectRoot] = process.argv.slice(2);
if (!workspaceRoot) {
throw new Error('Missing workspace root');
}
if (!projectRoot) {
throw new Error('Missing project root');
}
try {
const workspacePackage = JSON.parse(
readFileSync(join(workspaceRoot, 'package.json')).toString(),
);
const projectPackage = JSON.parse(
readFileSync(join(projectRoot, 'package.json')).toString(),
);
projectPackage.dependencies = workspacePackage.dependencies;
projectPackage.devDependencies = workspacePackage.devDependencies;
writeFileSync(
join(projectRoot, 'package.json'),
JSON.stringify(projectPackage, null, 2),
);
} catch (e) {
console.error('Error reading package.json file', e);
}