Skip to content

Commit

Permalink
feat(react-native): migrate to React Native 0.73
Browse files Browse the repository at this point in the history
Lost all changes just before finishing and had to do it again because the --skip-git-init option in RN cli isn't implemented yet 😭 release-npm

BREAKING CHANGE: Requires small migration to React Native 0.73, mainly creating a metro.config.cjs file.
  • Loading branch information
Matthias Giger committed Dec 8, 2023
1 parent b383444 commit d6f0453
Show file tree
Hide file tree
Showing 13 changed files with 164 additions and 88 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ npm init --yes now numic ./my-starter-app app # Tempalte with navigation, data,

This will prompt for an app name that can only contain **alphanumeric** characters and will be used as the initial bundle identifier. Using `NumicApp` as the name will result in `com.numicapp` as the bundle identifier. The name as well as the display name can later be configured in `app.json`.

> [!IMPORTANT]
> This project follows an alwasy up-to-date policy. Make sure to migrate to the newest React Native version when upgrading.
## Commands

This framework provides the following commands that will be added to `scripts` in `package.json` upon installation.
Expand Down
9 changes: 5 additions & 4 deletions configure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { join } from 'path'
import { formatPackageJson } from 'pakag'
import merge from 'deepmerge'
import parse from 'parse-gitignore'
import { parse as parseJsonWithComments } from 'json5'
import { basePath, options } from './helper'
import { userGitignore, filterPluginIgnores } from './configuration/gitignore'
import { packageJson } from './configuration/package'
Expand Down Expand Up @@ -46,7 +47,7 @@ export const configureTsConfig = () => {

// Make sure extended properties aren't duplicated.
try {
const extendedProperties = JSON.parse(readFileSync(rnTsconfigPath, 'utf-8'))
const extendedProperties = parseJsonWithComments(readFileSync(rnTsconfigPath, 'utf-8'))

// Avoid duplicate values.
Object.keys(configuration.compilerOptions).forEach((key) => {
Expand All @@ -60,7 +61,7 @@ export const configureTsConfig = () => {

if (Array.isArray(configuration.exclude)) {
configuration.exclude = configuration.exclude.filter(
(item: string) => !extendedProperties.exclude.includes(item)
(item: string) => !extendedProperties.exclude.includes(item),
)

if (configuration.exclude.length === 0) {
Expand Down Expand Up @@ -91,7 +92,7 @@ export const configureGitignore = () => {

if (existsSync(gitIgnorePath)) {
entries = filterPluginIgnores(
entries.concat(parse(readFileSync(gitIgnorePath, 'utf8')).patterns)
entries.concat(parse(readFileSync(gitIgnorePath, 'utf8')).patterns),
)
}

Expand All @@ -117,7 +118,7 @@ const configurePackageJson = async (isFirstInstall: boolean) => {
// Format with prettier and sort before writing.
writeFileSync(
join(basePath(), './package.json'),
await formatPackageJson(JSON.stringify(generatedPackageJson))
await formatPackageJson(JSON.stringify(generatedPackageJson)),
)

options().pkg = generatedPackageJson
Expand Down
53 changes: 53 additions & 0 deletions documentation/RELEASE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Release

## Release for Android

> [!TIP]
> The plugin already takes care of a lot of steps in the release process. In case you run into any issues or the below documenation might be out-of-date consult the [official React Native APK release documentation](https://reactnative.dev/docs/signed-apk-android).
To release a React Native app for Android a few manual steps are currently required. First you need JDK (not just the JRE bundled with Android Studio) downloaded and installed from [Oracle Downloads](https://www.oracle.com/java/technologies/downloads) and linked in `~/.zshrc` with `export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-[DOWNLOADED_VERSION].jdk/Contents/Home/bin/java`, apply changes with `source ~/.zshrc`. Once that's done generate a signing key:

```sh
sudo keytool -genkey -v -keystore my-upload-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000
```

Add the generated file to `/android/app/my-upload-key.keystore`, no need to commit this file as it will be integrated into the patch (it's fairly short). Once that's also done add the password you just entered as well as the file location to the end of `/android/gradle.properties`:

```sh

MYAPP_UPLOAD_STORE_FILE=my-upload-key.keystore
MYAPP_UPLOAD_KEY_ALIAS=my-key-alias
MYAPP_UPLOAD_STORE_PASSWORD=*****
MYAPP_UPLOAD_KEY_PASSWORD=*****
```

The make the following changes to `/android/app/build.gradle`:

```sh
keyAlias 'androiddebugkey'
keyPassword 'android'
}
+ release {
+ if (project.hasProperty('MYAPP_UPLOAD_STORE_FILE')) {
+ storeFile file(MYAPP_UPLOAD_STORE_FILE)
+ storePassword MYAPP_UPLOAD_STORE_PASSWORD
+ keyAlias MYAPP_UPLOAD_KEY_ALIAS
+ keyPassword MYAPP_UPLOAD_KEY_PASSWORD
+ }
+ }
}
buildTypes {
debug {
signingConfig signingConfigs.debug
}
release {
- // Caution! In production, you need to generate your own keystore file.
- // see https://reactnative.dev/docs/signed-apk-android.
- signingConfig signingConfigs.debug
+ signingConfig signingConfigs.release
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
```
This will read the keyfile when running the `Distribute` script and build with the release configuration. Don't forget to revert `JAVA_HOME` to the JRE location in Android Studio.
23 changes: 12 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"build": "padua build",
"postinstall": "skip-local-postinstall dist/installation.js",
"start": "padua watch",
"test": "vitest run --no-threads --dir test"
"test": "vitest run --pool=threads --poolOptions.threads.singleThread --dir test"
},
"padua": {
"entry": [
Expand All @@ -33,26 +33,27 @@
"command-exists": "^1.2.9",
"deepmerge": "^4.3.1",
"eslint-plugin-prettier": "^5.0.1",
"fast-glob": "^3.3.1",
"global-cache-dir": "^5.0.0",
"fast-glob": "^3.3.2",
"global-cache-dir": "^6.0.0",
"is-ci": "^3.0.1",
"logua": "^3.0.2",
"json5": "^2.2.3",
"logua": "^3.0.3",
"pakag": "^3.1.1",
"parse-gitignore": "^2.0.0",
"prettier": "^3.0.3",
"prettier": "^3.1.0",
"prompts": "^2.4.2",
"semver": "^7.5.4",
"semver-sort": "^1.0.0",
"skip-local-postinstall": "^2.0.4"
},
"devDependencies": {
"@types/command-exists": "^1.2.2",
"@types/prompts": "^2.4.7",
"@types/semver": "^7.5.4",
"@types/command-exists": "^1.2.3",
"@types/prompts": "^2.4.9",
"@types/semver": "^7.5.6",
"jest-fixture": "^4.1.0",
"padua": "^2.0.6",
"react-native": "^0.72.6",
"vitest": "^0.34.6"
"padua": "^2.0.7",
"react-native": "^0.73.0",
"vitest": "^1.0.2"
},
"peerDependencies": {
"react-native": ">= 0.69"
Expand Down
21 changes: 10 additions & 11 deletions plugin/bundle-id.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const searchForFileAndReplace = (
filePathGlob: string | string[],
matcher: RegExp,
replacement: string,
nativePath: string
nativePath: string,
) => {
const files = glob.sync(filePathGlob, {
cwd: nativePath,
Expand Down Expand Up @@ -57,7 +57,7 @@ export default async ({
const cleanVersion = semver.coerce(version).version
if (!cleanVersion || !semver.valid(cleanVersion) || !semver.gte(cleanVersion, '0.71.0')) {
log(
`bundleId can only be customized with React Native >= 0.71 while the current version is "${cleanVersion}"`
`bundleId can only be customized with React Native >= 0.71 while the current version is "${cleanVersion}"`,
)
return
}
Expand All @@ -67,31 +67,30 @@ export default async ({

appBuildGradleContents = appBuildGradleContents.replaceAll(
/namespace\s"[\w.]+"/g,
`namespace "${bundleId}"`
`namespace "${bundleId}"`,
)

appBuildGradleContents = appBuildGradleContents.replaceAll(
/applicationId\s"[\w.]+"/g,
`applicationId "${bundleId}"`
`applicationId "${bundleId}"`,
)

writeFileSync(appBuildGradleFilePath, appBuildGradleContents)

searchForFileAndReplace(
[
'android/app/src/*/java/com/*/ReactNativeFlipper.java',
'android/app/src/main/java/com/*/MainActivity.java',
'android/app/src/main/java/com/*/MainApplication.java',
'android/app/src/main/java/com/*/MainActivity.kt',
'android/app/src/main/java/com/*/MainApplication.kt',
],
/package\s[\w.]+;/g,
`package ${bundleId};`,
nativePath
/package\s[\w.]+/g,
`package ${bundleId}`,
nativePath,
)

searchForFileAndReplace(
'ios/*.xcodeproj/project.pbxproj',
/PRODUCT_BUNDLE_IDENTIFIER = "org\.reactjs\.native\.example\.\$\(PRODUCT_NAME:rfc1034identifier\)";/g,
`PRODUCT_BUNDLE_IDENTIFIER = ${bundleId};`,
nativePath
nativePath,
)
}
3 changes: 2 additions & 1 deletion template-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ export const cacheTemplate = (nativeOptions: NativeOptions) => {
// TODO necessary? writeFileSync(join(folders.numic, 'package.json'), '{ "name": "numic-native" }')

// DOC https://github.com/react-native-community/cli/blob/master/packages/cli/src/commands/init/index.ts
// TODO --skip-git-init <boolean> not yet implemented
try {
execSync(
`npx react-native init ${nativeOptions.appName} --skip-install --version ${nativeOptions.version}`,
`npx react-native init ${nativeOptions.appName} --skip-install --install-pods false --version ${nativeOptions.version}`,
{
cwd: directory,
encoding: 'utf8',
Expand Down
15 changes: 15 additions & 0 deletions template/app/metro.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config')

/**
* Metro configuration
* https://facebook.github.io/metro/docs/configuration
*
* @type {import('metro-config').MetroConfig}
*/
const config = {
resolver: {
unstable_enablePackageExports: true,
},
}

module.exports = mergeConfig(getDefaultConfig(__dirname), config)
20 changes: 10 additions & 10 deletions template/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,33 @@
}
},
"dependencies": {
"epic-language": "^0.4.0",
"mobx": "^6.10.2",
"epic-language": "^0.5.0",
"mobx": "^6.12.0",
"mobx-react-lite": "^4.0.5",
"react": "^18.2.0",
"react-native": "^0.72.6",
"react-native": "^0.73.0",
"reactigation": "^4.0.1",
"responsive-react-native": "^1.0.1"
},
"devDependencies": {
"@react-native/eslint-config": "^0.74.0",
"@testing-library/jest-native": "^5.4.3",
"@testing-library/react-native": "^12.3.0",
"@testing-library/react-native": "^12.4.1",
"@tsconfig/react-native": "^3.0.2",
"@types/jest": "^29.5.6",
"@types/react-native": "^0.72.5",
"@typescript-eslint/eslint-plugin": "^6.8.0",
"@typescript-eslint/parser": "^6.8.0",
"@types/jest": "^29.5.11",
"@types/react-native": "^0.72.8",
"@typescript-eslint/eslint-plugin": "^6.13.2",
"@typescript-eslint/parser": "^6.13.2",
"android-sdk-numic-plugin": "^1.0.3",
"babel-jest": "^29.7.0",
"eslint": "^8.52.0",
"eslint": "^8.55.0",
"eslint-plugin-prettier": "^5.0.1",
"icon-numic-plugin": "^1.4.3",
"jest": "^29.7.0",
"metro-react-native-babel-preset": "^0.77.0",
"numic": "latest",
"react-test-renderer": "^18.2.0",
"typescript": "^5.2.2"
"typescript": "^5.3.3"
},
"overrides": {
"chalk": "^4.1.2"
Expand Down
15 changes: 15 additions & 0 deletions template/default/metro.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config')

/**
* Metro configuration
* https://facebook.github.io/metro/docs/configuration
*
* @type {import('metro-config').MetroConfig}
*/
const config = {
resolver: {
unstable_enablePackageExports: true,
},
}

module.exports = mergeConfig(getDefaultConfig(__dirname), config)
16 changes: 8 additions & 8 deletions template/default/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,27 @@
},
"dependencies": {
"react": "^18.2.0",
"react-native": "^0.72.6"
"react-native": "^0.73.0"
},
"type": "module",
"devDependencies": {
"@react-native/eslint-config": "^0.74.0",
"@tsconfig/react-native": "^3.0.2",
"@types/jest": "^29.5.6",
"@types/react-native": "^0.72.5",
"@types/react-test-renderer": "^18.0.5",
"@typescript-eslint/eslint-plugin": "^6.8.0",
"@typescript-eslint/parser": "^6.8.0",
"@types/jest": "^29.5.11",
"@types/react-native": "^0.72.8",
"@types/react-test-renderer": "^18.0.7",
"@typescript-eslint/eslint-plugin": "^6.13.2",
"@typescript-eslint/parser": "^6.13.2",
"android-sdk-numic-plugin": "^1.0.3",
"babel-jest": "^29.7.0",
"eslint": "^8.52.0",
"eslint": "^8.55.0",
"eslint-plugin-prettier": "^5.0.1",
"icon-numic-plugin": "^1.4.3",
"jest": "^29.7.0",
"metro-react-native-babel-preset": "^0.77.0",
"numic": "latest",
"react-test-renderer": "18.2.0",
"typescript": "^5.2.2"
"typescript": "^5.3.3"
},
"metro": {},
"jest": {
Expand Down
14 changes: 7 additions & 7 deletions test/native.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ test('Creates patch for simple change in android and ios user folder.', async ()

const podfileContents = readFile('ios/Podfile')
const changedPodfileContents = podfileContents.replace(
':hermes_enabled => flags[:hermes_enabled],',
':something_else_enabled => true,'
'config = use_native_modules!',
'config = use_active_modules'
)

writeFile('ios/Podfile', changedPodfileContents)
Expand All @@ -86,16 +86,16 @@ test('Creates patch for simple change in android and ios user folder.', async ()

expect(patchContents).toContain('mavenCentral()')
expect(patchContents).toContain('navenUI()')
expect(patchContents).toContain('- :hermes_enabled => flags[:hermes_enabled],')
expect(patchContents).toContain('+ :something_else_enabled => true,')
expect(patchContents).toContain('- config = use_native_modules!')
expect(patchContents).toContain('+ config = use_active_modules')

// Restore initial native folder change.
writeFile('android/build.gradle', buildGradleContents)
writeFile('ios/Podfile', podfileContents)

expect(readFile('android/build.gradle')).not.toContain('navenUI()')
expect(readFile('android/build.gradle')).toContain('mavenCentral()')
expect(readFile('ios/Podfile')).not.toContain(':something_else_enabled => true,')
expect(readFile('ios/Podfile')).not.toContain('config = use_active_modules')

apply({})

Expand All @@ -105,8 +105,8 @@ test('Creates patch for simple change in android and ios user folder.', async ()
expect(patchedBuildGradleContents).not.toContain('mavenCentral()')
expect(patchedBuildGradleContents).toContain('navenUI()')

expect(patchedPodfileContents).not.toContain(':hermes_enabled => flags[:hermes_enabled],')
expect(patchedPodfileContents).toContain(':something_else_enabled => true,')
expect(patchedPodfileContents).not.toContain('config = use_native_modules!')
expect(patchedPodfileContents).toContain('config = use_active_modules')
})

test('Patches nested changes as well as file additions, renames and removals.', async () => {
Expand Down
Loading

1 comment on commit d6f0453

@vercel
Copy link

@vercel vercel bot commented on d6f0453 Dec 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

numic – ./

numic-git-main-tobua.vercel.app
numic.vercel.app
numic-tobua.vercel.app

Please sign in to comment.