bundo-appgen
v0.0.10
Published
Native app generator for React Native macOS
Maintainers
Readme
bundo-appgen
Generate native app project for React Native app host easily with a single JavaScript configuration, and a CLI.
Table of Contents
Requirements
- Node.js v22 or latest
- macOS
- (Optional) tsx if you want to use TypeScript configuration for
bundo-appgen
Technically, you can use Bun, but bundo-appgen is depending on Node.js Permission Model for bundo-appgen plugins. We may support for Bun fully until the permission model is supported. See Bun #6617 issue. You can still use Bun for local app development.
Installation
Install bundo-appgen in your project
Bun
bun install bundo-appgenpnpm
pnpm install bundo-appgennpm
npm install bundo-appgenFortunately, you can install bundo-appgen in your existing react-native-macos app, because bundo-appgen is a single independent package.
Usage
Configuration File
Create bundo.config.mjs file that lives beside of your package.json project
/**
* @type {import("bundo-appgen").Config.Data}
*/
const config = {
name: "Your App Name",
macos: {
bundleIdentifier: "com.yourcompany",
buildVersion: "1",
version: "1.0.0",
},
}
export default configTypeScript Configuration File
You can also use TypeScript config file, but you need tsx installed in your project as development dependency. Create bundo.config.mts or bundo.config.ts that lives beside of your package.json project
import {
Config,
} from "bundo-appgen"
export default {
name: "Your App Name",
macos: {
bundleIdentifier: "com.yourcompany",
buildVersion: "1",
version: "1.0.0",
},
} satisfies Config.Data Explore other options in the Config.Data type definitions, such as changing app icon, app fonts, Info Plist, etc. We will provide a well documentation later regarding this. You can visit our development playground for your references.
Command Execution
bundo-appgen provides a command line interface to generate native project directory as a React Native app host. If you have experience with Expo Continuous Native Generation, this is really similar to the expo prebuild command.
:warning: If you are using
bundo-appgenin your existingreact-native-macosapp, backup your entire project first, and remove the "macos" folder.
Now, generate your native app project by running this command
with Bun
bunx bundo-appgenwith pnpm
pnpx bundo-appgenwith npm
npx bundo-appgenThis command is doing these execution steps in order
- Retrieve and evaluate the configuration file
- Copy the native macos template from
bundo-appgento your project - Modifying some files to the native template files, such as project renaming
- Run
bundo-appgenplugin runner, and evaluating the result from plugins through IPC child process - Modify some template files, because a plugin may want to customize it
- Execute
xcodegencommand to generate .xcodeproj directory
Now, you should see "macos" generated folder by the appgen command.
Run bunx bundo-appgen --help for more informations.
Running the App
Register your React entry with AppRegistry in your index.js file with "main" name
import {
AppRegistry,
} from "react-native"
import App from "./App"
AppRegistry.registerComponent("main", () => App)Finally, you can run Metro server bun run start, and run the app via Xcode.
You can also run your app with command
bunx react-native run-macos --scheme HelloWorldThe target name ("HelloWorld") is the .xcworkspace folder name without the .xcworkspace in the /macos directory.
Background
We want to wrap a native project as a React Native app host easily without hurting so much times by touching the native code or native platform tooling e.g. changing app icon, app metadata, touching native C++, Swift & Objective-C code for upgrading React Native, etc.
This package is heavily inspired by the Expo Continuous Native Generation. Expo does a heavy lifting that make developers focus more on providing and delivering the actual product.
bundo-appgen also wants to get the same experience like Expo, but bundo-appgen is supporting the macOS (and Windows probably in the future) that Expo does not want to, not creating another layer for native module e.g. Expo Modules, and sanboxed plugin for security reasons. Another differentiation is bundo-appgen providing bare platform configurations as much as possible and keeping each platform configurations separated. This is much easier for prototyping the native app project because each platform have some unique configurations that other platforms don't have, and we can follow each platform requirements much easier. For an example, you can see a configuration file example below
import {
Config,
} from "bundo-appgen"
export default {
// the `name` is shared, but overwriting through
// the platform configuration is still possible,
// e.g. CFBundleDisplayName in macOS Info.plist
name: "Hello World App",
macos: {
appicon: "MyAppIcon",
assetCatalog: {
appiconset: [
Config.Apple.createOSXappiconset({
name: "MyAppIcon",
images: {
"1024x1024": "./path/to/your-image-1024.png",
"512x512": "./path/to/your-image-512.png",
"256x256": "./path/to/your-image-256.png",
"128x128": "./path/to/your-image-128.png",
"64x64": "./path/to/your-image-64.png",
"32x32": "./path/to/your-image-32.png",
"16x16": "./path/to/your-image-16.png",
},
}),
],
},
resources: [
"./node_modules/ui-module/assets/fonts",
],
infoPlist: {
ATSApplicationFontsPath: "fonts/",
NSMicrophoneUsageDescription: "We need to access your microphone, no questions!",
},
locales: ["en", "de"]
stringCatalogs: {
InfoPlist: {
NSMicrophoneUsageDescription: {
en: "We need to access your microphone, no questions!",
de: "Wir mussen auf ihr Mikrofon zugreiefen, ohne Wenn und Aber!"
},
},
}
},
windows: {
// later
},
visionos: {
// later
},
plugins: [],
} satisfies Config.Databundo-appgen is currently supporting macOS. Right after we are sure that our appgen (App Generator) for macOS is fully-ready-stabily, bundo-appgen will try to support the Windows platform, because it is really this project main plan at the time.
For Android and iOS, it is better to use Expo right now. It provides variety of first party packages in its ecosystem.
Sandboxed Plugin
Not to mention, bundo-appgen is also sandboxing the plugin invocation. We do not allow a plugin to perform an unrestricted action such as File System, Networking, Child Process, and other actions in Node.js. See Node.js Permission Model.
We only allow plugins to run their main function in restricted permission. Currently, we only allow this listed permission for plugin
- File System - Read only access to these directories and/or files
- <project>/macos/HelloWorld/AppDelegate.swift
- <project>/macos/Podfile
- node_modules directory lookup relatively from your project and global modules
- Their own plugin directory
A plugin can still modify the templated files, such as AppDelegate.swift file for the macOS, but a plugin will only be allowed to modify in a manner way through a raw JavaScript string that bundo-appgen gives to their main function, instead of using file system to write. For your references, you can see our bundo-window plugin main function to modify the AppDelegate.swift to make the macOS app has no title bar.
We create a module that what we called "plugin runner". From the bundo-appgen command, it will spawn another Node.js child process with restricted permission to run the plugin runner. The plugin runner will invoke all the plugins main function provided with a JavaScript object (context) argument given. When all the plugin invocation have finished, we send the result to the main process through Inter Process Communication, and then, the main process will be responsible to do the actual writing to the template files.
We choose this model because we choose the Zero Trust approach. We do not want a plugin performs a malicious action in your machine with reading and writing to any directories (file system), networking, and spawning a child process.
