Skip to content

Commit

Permalink
feat: add link-assets command (react-native-community#2276)
Browse files Browse the repository at this point in the history
* feat: add new link-assets bare command structure

* feat: add some libraries necessary for link assets feature

* feat: add `assets` option to CLI configuration types and schema

* feat: migrate Link Assets feature to TypeScript

* refactor: rename some clean/copy assets function options

* refactor: several refactors to improve original code readability

* refactor: rename linking flags

* add function distinguishing if project is using kotlin

* update code to handle MainApplication.kt

* update comments regarding MainApplication file

* remove slugify dependency

* Revert "remove slugify dependency"

This reverts commit 2fe7b95.

* replace all dashes with underscores when normalizing string

* refactor: refactor linkAssets, linkPlatform and add try/catches for fs operations

* refactor: refactor and move findPbxprojFile to cli-platform-apple

* test: update snapshots

* test: implement first test for linkAssets

* test: add empty tests

* test: implement test for java project

* test: implement new assets test

* test: implement unlink assets test

* test: implement unlink all assets test

* test: implement relink fonts test

* refactor: remove unused --project-root option from link-assets command

* refactor: improve createGroupWithMessage() log message

* docs: add documentation for link-assets

* docs: update projects.md documentation

* docs: update link-assets command description

* refactor: remove CHANGELOG.md

* test: simplifly tests

* refactor: address review comments

* refactor: reuse some logic between copy/clean android assets

* refactor: improve android copy/clean assets logic to handle resources file names correctly

* refactor: improve command messages

* refactor: create type for platform

* fix: add all link options for ios

* fix: fix not configured assets log message

* Remove link assets from main endpoint

* test: minor fix to e2e test

* chore: update package.json

* Revert "test: minor fix to e2e test"

This reverts commit 376e1ee.

* update config test with path

* update snapshot

---------

Co-authored-by: Tomasz Misiukiewicz <tomasz.misiukiewicz@callstack.com>
  • Loading branch information
fabioh8010 and TMisiukiewicz committed Mar 29, 2024
1 parent d64022f commit 6c42d63
Show file tree
Hide file tree
Showing 75 changed files with 5,124 additions and 7 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ And then:

```sh
cd /my/new/react-native/project/
yarn link "@react-native-community/cli-platform-ios" "@react-native-community/cli-platform-android" "@react-native-community/cli" "@react-native-community/cli-server-api" "@react-native-community/cli-types" "@react-native-community/cli-tools" "@react-native-community/cli-debugger-ui" "@react-native-community/cli-clean" "@react-native-community/cli-doctor" "@react-native-community/cli-config" "@react-native-community/cli-platform-apple"
yarn link "@react-native-community/cli-platform-ios" "@react-native-community/cli-platform-android" "@react-native-community/cli" "@react-native-community/cli-server-api" "@react-native-community/cli-types" "@react-native-community/cli-tools" "@react-native-community/cli-debugger-ui" "@react-native-community/cli-clean" "@react-native-community/cli-doctor" "@react-native-community/cli-config" "@react-native-community/cli-platform-apple" "@react-native-community/cli-link-assets"
```

Once you're done with testing and you'd like to get back to regular setup, run `yarn unlink` instead of `yarn link` from above command. Then `yarn install --force`.
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ React Native CLI is a dependency of `react-native`, which makes it a transitive
"@react-native-community/cli-config": "VERSION",
"@react-native-community/cli-debugger-ui": "VERSION",
"@react-native-community/cli-doctor": "VERSION",
"@react-native-community/cli-link-assets": "VERSION",
"@react-native-community/cli-platform-android": "VERSION",
"@react-native-community/cli-platform-ios": "VERSION",
"@react-native-community/cli-server-api": "VERSION",
Expand Down
7 changes: 5 additions & 2 deletions __e2e__/__snapshots__/config.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -79,16 +79,19 @@ exports[`shows up current config without unnecessary output 1`] = `
"ios": {},
"android": {}
},
"assets": [],
"project": {
"ios": {
"sourceDir": "<<REPLACED_ROOT>>/TestProject/ios"
"sourceDir": "<<REPLACED_ROOT>>/TestProject/ios",
"assets": []
},
"android": {
"sourceDir": "<<REPLACED_ROOT>>/TestProject/android",
"appName": "app",
"packageName": "com.testproject",
"applicationId": "com.testproject",
"mainActivity": ".MainActivity"
"mainActivity": ".MainActivity",
"assets": []
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions __e2e__/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,12 @@ test('shows up current config without unnecessary output', () => {
? {
name: 'TestProject.xcworkspace',
isWorkspace: true,
path: '.',
}
: {
name: 'TestProject.xcodeproj',
isWorkspace: false,
path: '.',
};

expect(parsedStdout.project.ios.xcodeProject).toStrictEqual(
Expand Down
1 change: 1 addition & 0 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ React Native CLI comes with following commands:
- [`clean`](/packages/cli-clean/README.md#clean)
- [`config`](/packages/cli-config/README.md#config)
- [`doctor`](/packages/cli-doctor/README.md#doctor)
- [`link-assets`](/packages/cli-link-assets/README.md#link-assets)
- [`init`](#init)
- [`info`](/packages/cli-doctor/README.md#info)
- [`log-android`](/packages/cli-platform-android/README.md#log-android)
Expand Down
15 changes: 15 additions & 0 deletions docs/projects.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type ProjectConfigT = {
[key: string]: DependencyConfig;
};
commands: Command[];
assets?: string[];
};
```

Expand All @@ -69,6 +70,7 @@ type IOSProjectParams = {
sourceDir?: string;
watchModeCommandParams?: string[];
automaticPodsInstallation?: boolean;
assets?: string[];
};

type AndroidProjectParams = {
Expand All @@ -78,6 +80,7 @@ type AndroidProjectParams = {
packageName?: string;
dependencyConfiguration?: string;
watchModeCommandParams?: string[];
assets?: string[];
};
```

Expand Down Expand Up @@ -116,6 +119,10 @@ If set to `true`, you can skip running `pod install` manually whenever it's need

> Note: Starting from React Native 0.73, CLI's `init` command scaffolds the project with `react-native.config.js` file with this value set to `true` by default. Older projects can opt-in after migrating to 0.73. Please note that if your setup does not follow the standard React Native template, e.g. you are not using Gems to install CocoaPods, this might not work properly for you.
### project.ios.assets

Array of folder paths that will be passed to the `npx react-native link-assets` command to specify the assets to be linked to iOS project.

#### project.android.appName

A name of the app in the Android `sourceDir`, equivalent to Gradle project name. By default it's `app`.
Expand Down Expand Up @@ -155,6 +162,10 @@ The list should contain the name of the components, as they're registered in the

Since React Native 0.74, this property is ignored as the Interop Layer is **Automatic**, you don't need to register the Legacy Components anymore and they will be discovered automatically.

### project.android.assets

Array of folder paths that will be passed to the `npx react-native link-assets` command to specify the assets to be linked to Android project.

### platforms

A object with platforms defined inside a project. You can check the format and options available [`here`](platforms.md#platform-interface)
Expand Down Expand Up @@ -220,3 +231,7 @@ module.exports = {
The object provided here is deep merged with the dependency config. Check [`projectConfig`](platforms.md#projectconfig) and [`dependencyConfig`](platforms.md#dependencyConfig) return values for a full list of properties that you can override.

> Note: This is an advanced feature and you should not need to use it most of the time.
### assets

Array of folder paths that will be passed to the `npx react-native link-assets` command to specify the assets to be linked to Android / iOS projects.
2 changes: 1 addition & 1 deletion jest/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const cleanup = (directory: string) => {
*/
export const writeFiles = (
directory: string,
files: {[filename: string]: string},
files: {[filename: string]: string | NodeJS.ArrayBufferView},
) => {
createDirectory(directory);
Object.keys(files).forEach((fileOrPath) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Object {

exports[`should have a valid structure by default 1`] = `
Object {
"assets": Array [],
"commands": Array [],
"dependencies": Object {},
"healthChecks": Array [],
Expand Down
1 change: 1 addition & 0 deletions packages/cli-config/src/loadConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ function loadConfig(projectRoot: string = findProjectRoot()): Config {
commands: userConfig.commands,
healthChecks: userConfig.healthChecks || [],
platforms: userConfig.platforms,
assets: userConfig.assets,
get project() {
if (lazyProject) {
return lazyProject;
Expand Down
2 changes: 2 additions & 0 deletions packages/cli-config/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ export const projectConfig = t
.items(t.string())
.optional(),
automaticPodsInstallation: t.bool().default(false),
assets: t.array().items(t.string()).default([]),
})
.default({}),
android: t
Expand All @@ -177,6 +178,7 @@ export const projectConfig = t
.array()
.items(t.string())
.optional(),
assets: t.array().items(t.string()).default([]),
})
.default({}),
})
Expand Down
109 changes: 109 additions & 0 deletions packages/cli-link-assets/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# @react-native-community/cli-link-assets

This package is part of the [React Native CLI](../../README.md). It contains commands to link assets to your Android / iOS projects.

## Installation

```sh
yarn add @react-native-community/cli-link-assets
```

## Commands

### `link-assets`

Usage:

```sh
npx react-native link-assets
```

Links your assets to the Android / iOS projects. You must configure your `react.native.config.js` file to specify where your assets are located, e.g:

```js
module.exports = {
// If you want to link assets to both platforms.
assets: ['./assets/shared'],
project: {
android: {
// If you want to link assets only to Android.
assets: ['./assets/android'],
},
ios: {
// If you want to link assets only to iOS.
assets: ['./assets/ios'],
automaticPodsInstallation: true,
},
},
};
```

### Android

For **Android**, it supports the linking of the following assets:

#### Fonts (OTF, TTF)

Font assets are linked in Android by using [XML resources](https://developer.android.com/develop/ui/views/text-and-emoji/fonts-in-xml). For instance, if you add the **Lato** font to your project, it will generate a `lato.xml` file in `android/app/src/main/res/font/` folder with all the font variants that you added. It will also add a method call in `MainApplication.kt` or `MainApplication.java` file in order to register the custom font during the app initialization. It will look something like this:

```kotlin
// other imports

import com.facebook.react.common.assets.ReactFontManager // <- imports ReactFontManager.

class MainApplication : Application(), ReactApplication {

// other methods

override fun onCreate() {
super.onCreate()
ReactFontManager.getInstance().addCustomFont(this, "Lato", R.font.lato) // <- registers the custom font.
// ...
}
}
```

In this case, `Lato` is what you have to set in the `fontFamily` style of your `Text` component. To select the font variant e.g. weight and style, use `fontWeight` and `fontStyle` styles respectively.

```jsx
<Text style={{ fontFamily: 'Lato', fontWeight: '700', fontStyle: 'italic' }}>Lato Bold Italic</Text>
```

> [!IMPORTANT]
> If you have already linked font assets in your Android project with [react-native-asset](https://github.com/unimonkiez/react-native-asset), when running this tool it will relink your fonts to use XML resources as described above. **This migration will allow you to use your fonts in the code the same way you would use it for iOS**. Please update your code to use `fontFamily`, `fontWeight` and `fontStyle` styles correctly.
#### Images (JPG, PNG, GIF)

Image assets are linked by copying them to `android/app/src/main/res/drawable/` folder. This can be useful in brownfield applications where you need to use assets in the native side.

#### Sounds (MP3)

Sound assets are linked by copying them to `android/app/src/main/res/raw/` folder. This can be useful if used together with [react-native-sound](https://github.com/zmxv/react-native-sound) or in brownfield applications where you need to use assets in the native side.

#### Others

Any other custom assets are linked by copying them to `android/app/src/main/assets/custom/` folder.

### iOS

For **iOS**, it supports the linking of the following assets:

#### Fonts (OTF, TTF)

Font assets are linked in iOS by editing `project.pbxproj` and `Info.plist` files. To use the font in your app, you can a combination of `fontFamily`, `fontWeight` and `fontStyle` styles in the same way you would use for Android. In case you didn't link your font assets in Android and you are not sure which value you have to set in `fontFamily` style, you can use `Font Book` app in your Mac to find out the correct value by looking the `Family Name` property.

#### Images (JPG, PNG, GIF)

Image assets are linked by editing `project.pbxproj` and adding them as Resources. This can be useful in brownfield applications where you need to use assets in the native side.

#### Sounds (MP3)

Image assets are linked by editing `project.pbxproj` and adding them as Resources. This can be useful if used together with [react-native-sound](https://github.com/zmxv/react-native-sound) or in brownfield applications where you need to use assets in the native side.

#### Others

Image assets are linked by editing `project.pbxproj` and adding them as Resources.

### Manifest files

In both platforms, linked assets are tracked using the `link-assets-manifest.json` files which are stored in both `android/` and `ios/` folders. **They are necessary to track which assets are currently linked, and if the tool needs to add new ones or remove old assets during linking process, so make sure to commit them!**
39 changes: 39 additions & 0 deletions packages/cli-link-assets/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "@react-native-community/cli-link-assets",
"version": "14.0.0-alpha.2",
"license": "MIT",
"main": "build/index.js",
"publishConfig": {
"access": "public"
},
"types": "build/index.d.ts",
"dependencies": {
"@react-native-community/cli-config": "14.0.0-alpha.2",
"@react-native-community/cli-platform-android": "14.0.0-alpha.2",
"@react-native-community/cli-platform-apple": "14.0.0-alpha.2",
"@react-native-community/cli-platform-ios": "14.0.0-alpha.2",
"@react-native-community/cli-tools": "14.0.0-alpha.2",
"chalk": "^4.1.2",
"fast-xml-parser": "^4.3.2",
"opentype.js": "^1.3.4",
"plist": "^3.1.0",
"xcode": "^3.0.1"
},
"files": [
"build",
"!*.d.ts",
"!*.map"
],
"devDependencies": {
"@react-native-community/cli-types": "14.0.0-alpha.2",
"@types/opentype.js": "^1.3.8",
"@types/plist": "^3.0.5",
"type-fest": "^4.10.2"
},
"homepage": "https://github.com/react-native-community/cli/tree/main/packages/cli-link-assets",
"repository": {
"type": "git",
"url": "https://github.com/react-native-community/cli.git",
"directory": "packages/cli-link-assets"
}
}
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 51 additions & 0 deletions packages/cli-link-assets/src/__fixtures__/files/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>RN0730</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string></string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sound.mp3
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.example;

import android.app.Application;
import com.facebook.react.ReactApplication;

public class MainApplication extends Application implements ReactApplication {
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
}
}
11 changes: 11 additions & 0 deletions packages/cli-link-assets/src/__fixtures__/files/MainApplication.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example

import android.app.Application
import com.facebook.react.ReactApplication

class MainApplication : Application(), ReactApplication {
override fun onCreate() {
super.onCreate()
SoLoader.init(this, false)
}
}
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Loading

0 comments on commit 6c42d63

Please sign in to comment.