├── .changeset ├── README.md ├── config.json ├── swift-drinks-juggle.md └── thin-rockets-cover.md ├── .eslintrc.js ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── app.plugin.js ├── build ├── index.d.ts └── index.js ├── bun.lockb ├── example ├── .gitignore ├── App.tsx ├── app.json ├── assets │ ├── adaptive-icon.png │ ├── animations │ │ ├── icon.png │ │ └── truck_v7.riv │ ├── favicon.png │ ├── icon.png │ └── splash.png ├── babel.config.js ├── bun.lockb ├── package.json └── tsconfig.json ├── expo-module.config.json ├── package.json ├── src └── index.ts └── tsconfig.json /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "public", 8 | "baseBranch": "main", 9 | "updateInternalDependencies": "patch", 10 | "ignore": [] 11 | } 12 | -------------------------------------------------------------------------------- /.changeset/swift-drinks-juggle.md: -------------------------------------------------------------------------------- 1 | --- 2 | "expo-custom-assets": minor 3 | --- 4 | 5 | added ignoredPattern prop 6 | -------------------------------------------------------------------------------- /.changeset/thin-rockets-cover.md: -------------------------------------------------------------------------------- 1 | --- 2 | "expo-custom-assets": patch 3 | --- 4 | 5 | fix undefined ignoredPattern issue 6 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // @generated by expo-module-scripts 2 | module.exports = require('expo-module-scripts/eslintrc.base.js'); 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # expo-custom-assets 2 | 3 | ## 1.3.3 4 | 5 | ### Patch Changes 6 | 7 | - e675846: adding license to npm 8 | 9 | ## 1.3.0 10 | 11 | ### Minor Changes 12 | 13 | - Changing assets path to an array 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Malaa-Tech 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # expo-custom-assets 2 | 3 | Expo plugin to add custom assets to xcode and android studio to allow the usage of it as local files for projects like [Rive](https://help.rive.app/runtimes/overview/react-native/adding-rive-to-expo) 4 | 5 | # Demo 6 | 7 | 8 | 9 | # API documentation 10 | 11 | - This plugin requires assets path only nothing more 12 | 13 | ### Add the package to your npm dependencies 14 | 15 | ``` 16 | npm install expo-custom-assets 17 | yarn add expo-custom-assets 18 | pnpm install expo-custom-assets 19 | bun install expo-custom-assets 20 | ``` 21 | 22 | # Installation in managed Expo projects 23 | 24 | For [managed](https://docs.expo.dev/archive/managed-vs-bare/) Expo projects 25 | 26 | - After installing the package through npm add the plugin to your app.json 27 | 28 | ```json 29 | "plugins": [ 30 | [ 31 | "expo-custom-assets", 32 | { 33 | // Add asset directory paths, the plugin copies the files in the given paths to the app bundle folder named Assets 34 | "assetsPaths": ["./path-to-your-asset-folder"] 35 | // if you want custom assets directory name use this prop (default: "Assets") 36 | // "assetsDirName": "testName" 37 | } 38 | ] 39 | ] 40 | ``` 41 | 42 | # Installation in bare React Native projects 43 | 44 | For bare React Native projects, you must ensure that you have [installed and configured the `expo` package](https://docs.expo.dev/bare/installing-expo-modules/) before continuing. 45 | 46 | # Contributing 47 | 48 | Contributions are very welcome! we have some things to do in our [Todo's](#Todo's) 49 | 50 | # Todo's 51 | 52 | - [x] Stop creating Assets folder in it exist 53 | - [x] Allow for custom asset folder name 54 | - [x] Fix typescript error when building the package 55 | - [ ] Add more example for other custom assets other than Rive 56 | 57 | # Credits 58 | 59 | - For the android part I almost used the plugin from [MortadhaFadhlaoui](https://github.com/MortadhaFadhlaoui) at with small modifications 60 | - For ios part I really benefited from 61 | - ChatGpt for the great help with making the script work as I wanted 62 | -------------------------------------------------------------------------------- /app.plugin.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./build"); 2 | -------------------------------------------------------------------------------- /build/index.d.ts: -------------------------------------------------------------------------------- 1 | import { type ConfigPlugin } from "@expo/config-plugins"; 2 | declare const _default: ConfigPlugin<{ 3 | assetsPaths: string[]; 4 | assetsDirName?: string; 5 | ignoredPattern?: string; 6 | }>; 7 | export default _default; 8 | -------------------------------------------------------------------------------- /build/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || (function () { 19 | var ownKeys = function(o) { 20 | ownKeys = Object.getOwnPropertyNames || function (o) { 21 | var ar = []; 22 | for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; 23 | return ar; 24 | }; 25 | return ownKeys(o); 26 | }; 27 | return function (mod) { 28 | if (mod && mod.__esModule) return mod; 29 | var result = {}; 30 | if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); 31 | __setModuleDefault(result, mod); 32 | return result; 33 | }; 34 | })(); 35 | Object.defineProperty(exports, "__esModule", { value: true }); 36 | const config_plugins_1 = require("@expo/config-plugins"); 37 | const fs_extra_1 = require("fs-extra"); 38 | const path = __importStar(require("node:path")); 39 | function withCustomAssetsAndroid(config, props) { 40 | const { assetsPaths, ignoredPattern } = props; 41 | return (0, config_plugins_1.withDangerousMod)(config, [ 42 | "android", 43 | async (config) => { 44 | const { projectRoot } = config.modRequest; 45 | const resDir = path.join(projectRoot, "android", "app", "src", "main", "res"); 46 | const rawDir = path.join(resDir, "raw"); 47 | (0, fs_extra_1.ensureDirSync)(rawDir); 48 | for (const assetSourceDir of assetsPaths) { 49 | const assetSourcePath = path.join(projectRoot, assetSourceDir); 50 | const assetFiles = (await (0, fs_extra_1.readdir)(assetSourcePath)).filter(file => ignoredPattern === undefined ? true : !file.match(new RegExp(ignoredPattern))); 51 | for (const assetFile of assetFiles) { 52 | const srcAssetPath = path.join(assetSourcePath, assetFile); 53 | const destAssetPath = path.join(rawDir, assetFile); 54 | (0, fs_extra_1.copyFileSync)(srcAssetPath, destAssetPath); 55 | } 56 | } 57 | return config; 58 | }, 59 | ]); 60 | } 61 | function withCustomAssetsIos(config, props) { 62 | const { assetsPaths, assetsDirName, ignoredPattern } = props; 63 | return (0, config_plugins_1.withXcodeProject)(config, async (config) => { 64 | const { projectRoot } = config.modRequest; 65 | const iosDir = path.join(projectRoot, "ios"); 66 | const assetsDir = path.join(iosDir, assetsDirName ?? "Assets"); 67 | (0, fs_extra_1.ensureDirSync)(assetsDir); 68 | for (const assetSourceDir of assetsPaths) { 69 | const assetSourcePath = path.join(projectRoot, assetSourceDir); 70 | // const assetFiles = await readdir(assetSourcePath); 71 | const assetFiles = (await (0, fs_extra_1.readdir)(assetSourcePath)).filter(file => ignoredPattern === undefined ? true : !file.match(new RegExp(ignoredPattern))); 72 | const project = config.modResults; 73 | const groupName = "Assets"; 74 | for (const assetFile of assetFiles) { 75 | const assetPath = path.join(assetSourceDir, assetFile); 76 | const destAssetPath = path.join(assetsDir, assetFile); 77 | (0, fs_extra_1.copyFileSync)(assetPath, destAssetPath); 78 | config_plugins_1.IOSConfig.XcodeUtils.addResourceFileToGroup({ 79 | filepath: destAssetPath, 80 | groupName, 81 | project, 82 | isBuildFile: true, 83 | verbose: true, 84 | }); 85 | } 86 | } 87 | return config; 88 | }); 89 | } 90 | const withCustomAssets = (config, props) => { 91 | // biome-ignore lint/style/noParameterAssign: this is the way :p 92 | config = withCustomAssetsIos(config, props); 93 | // biome-ignore lint/style/noParameterAssign: this is the way :p 94 | config = withCustomAssetsAndroid(config, props); 95 | return config; 96 | }; 97 | let pkg = { 98 | name: "expo-custom-assets", 99 | }; 100 | try { 101 | pkg = require("expo-custom-assets/package.json"); 102 | } 103 | catch { 104 | console.error("Failed to load package.json for expo-custom-assets"); 105 | } 106 | exports.default = (0, config_plugins_1.createRunOncePlugin)(withCustomAssets, pkg.name, pkg.version); 107 | -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Malaa-tech/expo-custom-assets/fbf46c5098cdc499bd860daf26315e001368c37e/bun.lockb -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | ios 3 | android 4 | .expo/ -------------------------------------------------------------------------------- /example/App.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { StatusBar } from "expo-status-bar"; 3 | import { Button, StyleSheet, Text, View } from "react-native"; 4 | import Rive from "rive-react-native"; 5 | 6 | export default function App() { 7 | const [animation, setAnimation] = useState("bouncing"); 8 | 9 | return ( 10 | 11 | 12 | Rive truk example 13 | 22 | 29 |