create-react-native-airborne 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/package.json +1 -1
- package/src/index.mjs +15 -2
- package/template/.envrc +1 -0
- package/template/.github/workflows/ci.yml +1 -1
- package/template/README.md +30 -11
- package/template/flake.lock +104 -0
- package/template/flake.nix +164 -0
- package/template/gitignore +1 -0
- package/template/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,6 +12,9 @@ bun create react-native-airborne my-app
|
|
|
12
12
|
|
|
13
13
|
- `--skip-install`: skip `bun install --workspaces`
|
|
14
14
|
- `--no-git`: skip `git init`
|
|
15
|
+
- `--nix`: include root-level `flake.nix`, `flake.lock`, and `.envrc`
|
|
16
|
+
|
|
17
|
+
If you use `--nix`, run `direnv allow` after `cd` into the generated project.
|
|
15
18
|
|
|
16
19
|
## Development
|
|
17
20
|
|
package/package.json
CHANGED
package/src/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { mkdir, readdir, readFile, writeFile, copyFile, stat } from "node:fs/promises";
|
|
2
|
+
import { mkdir, readdir, readFile, writeFile, copyFile, rm, stat } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { spawnSync } from "node:child_process";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -9,7 +9,9 @@ const flags = new Set(args.filter((arg) => arg.startsWith("--")));
|
|
|
9
9
|
const projectNameArg = args.find((arg) => !arg.startsWith("--"));
|
|
10
10
|
|
|
11
11
|
if (!projectNameArg) {
|
|
12
|
-
console.error(
|
|
12
|
+
console.error(
|
|
13
|
+
"Usage: bun create react-native-airborne <project-name> [--skip-install] [--no-git] [--nix]",
|
|
14
|
+
);
|
|
13
15
|
process.exit(1);
|
|
14
16
|
}
|
|
15
17
|
|
|
@@ -18,6 +20,7 @@ const projectSlug = projectName.toLowerCase().replace(/[^a-z0-9-]/g, "-").replac
|
|
|
18
20
|
const targetDir = path.resolve(process.cwd(), projectName);
|
|
19
21
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
20
22
|
const templateDir = path.resolve(__dirname, "../template");
|
|
23
|
+
const enableNix = flags.has("--nix");
|
|
21
24
|
|
|
22
25
|
async function exists(filePath) {
|
|
23
26
|
try {
|
|
@@ -81,6 +84,13 @@ if (await exists(targetDir)) {
|
|
|
81
84
|
await copyDirectory(templateDir, targetDir);
|
|
82
85
|
await replaceTokens(targetDir);
|
|
83
86
|
|
|
87
|
+
if (!enableNix) {
|
|
88
|
+
const optionalNixFiles = [".envrc", "flake.nix", "flake.lock"];
|
|
89
|
+
for (const file of optionalNixFiles) {
|
|
90
|
+
await rm(path.join(targetDir, file), { force: true });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
84
94
|
if (!flags.has("--skip-install")) {
|
|
85
95
|
const install = spawnSync("bun", ["install", "--workspaces"], {
|
|
86
96
|
cwd: targetDir,
|
|
@@ -101,4 +111,7 @@ console.log("\nNext steps:");
|
|
|
101
111
|
console.log(` cd ${projectName}`);
|
|
102
112
|
console.log(" cp client/.env.example client/.env");
|
|
103
113
|
console.log(" cp server/.env.example server/.env");
|
|
114
|
+
if (enableNix) {
|
|
115
|
+
console.log(" direnv allow");
|
|
116
|
+
}
|
|
104
117
|
console.log(" just dev");
|
package/template/.envrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
use flake . --no-pure-eval
|
package/template/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# React Native Airborne
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
React Native Airborne my opinionated mobile starter for folks who want to ship iOS/Android apps fast without repeating the same setup every project.
|
|
4
|
+
|
|
5
|
+
It includes a production-ready Expo client and a Convex backend.
|
|
4
6
|
|
|
5
7
|
## 🧰 Stack
|
|
6
8
|
|
|
@@ -11,17 +13,31 @@ Opinionated React Native starter for mobile-first apps with Expo + Convex.
|
|
|
11
13
|
- Convex backend + `convex-test`
|
|
12
14
|
- Zustand + MMKV persistence
|
|
13
15
|
- Expo push notifications
|
|
16
|
+
- Strict ESLint + Prettier setup
|
|
14
17
|
|
|
15
|
-
##
|
|
18
|
+
## 🎯 Opinionated Defaults
|
|
16
19
|
|
|
17
|
-
|
|
20
|
+
- Mobile-only target (iOS/Android), no web target in starter scope.
|
|
21
|
+
- Expo prebuild supported for local native debugging, but `client/ios` and `client/android` are not committed.
|
|
22
|
+
- Clerk + Convex integration wired from day one.
|
|
23
|
+
- Theme system includes `light`, `dark`, and `system` with persisted preference.
|
|
24
|
+
- Auth tokens are kept in secure storage flows, not MMKV.
|
|
25
|
+
|
|
26
|
+
## 🗂️ Project Layout
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
__APP_NAME__/
|
|
30
|
+
client/ # Expo app (Expo Router + Native Tabs)
|
|
31
|
+
server/ # Convex backend
|
|
32
|
+
```
|
|
18
33
|
|
|
19
34
|
## ✅ Prerequisites
|
|
20
35
|
|
|
21
36
|
- Bun `1.3.4+`
|
|
22
37
|
- `just` command runner
|
|
23
38
|
- Expo toolchain for iOS/Android simulators
|
|
24
|
-
- Clerk app
|
|
39
|
+
- Clerk app configured for native API
|
|
40
|
+
- Convex project/deployment
|
|
25
41
|
|
|
26
42
|
## ⚡ Quickstart
|
|
27
43
|
|
|
@@ -38,7 +54,7 @@ cd server
|
|
|
38
54
|
bun run dev
|
|
39
55
|
```
|
|
40
56
|
|
|
41
|
-
|
|
57
|
+
Run both client and server:
|
|
42
58
|
|
|
43
59
|
```bash
|
|
44
60
|
just dev
|
|
@@ -53,7 +69,7 @@ just dev
|
|
|
53
69
|
- `just prebuild`: generate local iOS/Android native folders
|
|
54
70
|
- `just ios`: launch iOS app
|
|
55
71
|
- `just android`: launch Android app
|
|
56
|
-
- `just lint`: lint
|
|
72
|
+
- `just lint`: lint checks
|
|
57
73
|
- `just typecheck`: TypeScript checks
|
|
58
74
|
- `just test`: client + server tests
|
|
59
75
|
- `just test-client`: client tests only
|
|
@@ -72,23 +88,26 @@ just prebuild
|
|
|
72
88
|
|
|
73
89
|
## 🔐 Environment Variables
|
|
74
90
|
|
|
75
|
-
|
|
91
|
+
Client (`client/.env`):
|
|
76
92
|
|
|
77
93
|
- `EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY`
|
|
78
94
|
- `EXPO_PUBLIC_CONVEX_URL`
|
|
79
95
|
- `EXPO_PUBLIC_EAS_PROJECT_ID` (optional)
|
|
80
96
|
|
|
81
|
-
|
|
97
|
+
Server (`server/.env`):
|
|
82
98
|
|
|
83
99
|
- `CLERK_JWT_ISSUER_DOMAIN`
|
|
84
100
|
- `EXPO_PUSH_ENDPOINT` (optional)
|
|
85
101
|
- `EXPO_ACCESS_TOKEN` (optional)
|
|
86
102
|
|
|
103
|
+
## 🤝 Contributor Guide
|
|
104
|
+
|
|
105
|
+
Detailed implementation and maintenance notes for engineers/agents live in `AGENTS.md`.
|
|
106
|
+
|
|
87
107
|
## 📝 Notes
|
|
88
108
|
|
|
89
|
-
- Mobile-only target (iOS/Android).
|
|
90
|
-
- Do not store sensitive auth tokens in MMKV.
|
|
91
109
|
- Uniwind classes are enabled by `client/global.css` and `client/metro.config.js`.
|
|
92
110
|
- `SafeAreaView` is wrapped with `withUniwind` in `client/src/components/screen.tsx` for className support.
|
|
93
111
|
- `server/convex/_generated` ships with starter stubs so typecheck/tests pass before deployment setup.
|
|
94
|
-
|
|
112
|
+
- After connecting Convex, run `cd server && bun run codegen` to regenerate server types.
|
|
113
|
+
- `.direnv/` is gitignored by default.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
{
|
|
2
|
+
"nodes": {
|
|
3
|
+
"android-nixpkgs": {
|
|
4
|
+
"inputs": {
|
|
5
|
+
"devshell": "devshell",
|
|
6
|
+
"flake-utils": "flake-utils",
|
|
7
|
+
"nixpkgs": [
|
|
8
|
+
"nixpkgs"
|
|
9
|
+
]
|
|
10
|
+
},
|
|
11
|
+
"locked": {
|
|
12
|
+
"lastModified": 1770842373,
|
|
13
|
+
"narHash": "sha256-uCxNWY8xGGtk8YwDJXsfpKUYw8GqR/3degPbIthtFTE=",
|
|
14
|
+
"owner": "tadfisher",
|
|
15
|
+
"repo": "android-nixpkgs",
|
|
16
|
+
"rev": "12878f0d54f923dff305b71d04c6d837df8ce5ab",
|
|
17
|
+
"type": "github"
|
|
18
|
+
},
|
|
19
|
+
"original": {
|
|
20
|
+
"owner": "tadfisher",
|
|
21
|
+
"repo": "android-nixpkgs",
|
|
22
|
+
"type": "github"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"devshell": {
|
|
26
|
+
"inputs": {
|
|
27
|
+
"nixpkgs": [
|
|
28
|
+
"android-nixpkgs",
|
|
29
|
+
"nixpkgs"
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
"locked": {
|
|
33
|
+
"lastModified": 1768818222,
|
|
34
|
+
"narHash": "sha256-460jc0+CZfyaO8+w8JNtlClB2n4ui1RbHfPTLkpwhU8=",
|
|
35
|
+
"owner": "numtide",
|
|
36
|
+
"repo": "devshell",
|
|
37
|
+
"rev": "255a2b1725a20d060f566e4755dbf571bbbb5f76",
|
|
38
|
+
"type": "github"
|
|
39
|
+
},
|
|
40
|
+
"original": {
|
|
41
|
+
"owner": "numtide",
|
|
42
|
+
"repo": "devshell",
|
|
43
|
+
"type": "github"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"flake-utils": {
|
|
47
|
+
"inputs": {
|
|
48
|
+
"systems": "systems"
|
|
49
|
+
},
|
|
50
|
+
"locked": {
|
|
51
|
+
"lastModified": 1731533236,
|
|
52
|
+
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
|
53
|
+
"owner": "numtide",
|
|
54
|
+
"repo": "flake-utils",
|
|
55
|
+
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
|
56
|
+
"type": "github"
|
|
57
|
+
},
|
|
58
|
+
"original": {
|
|
59
|
+
"owner": "numtide",
|
|
60
|
+
"repo": "flake-utils",
|
|
61
|
+
"type": "github"
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"nixpkgs": {
|
|
65
|
+
"locked": {
|
|
66
|
+
"lastModified": 1770843696,
|
|
67
|
+
"narHash": "sha256-LovWTGDwXhkfCOmbgLVA10bvsi/P8eDDpRudgk68HA8=",
|
|
68
|
+
"owner": "NixOS",
|
|
69
|
+
"repo": "nixpkgs",
|
|
70
|
+
"rev": "2343bbb58f99267223bc2aac4fc9ea301a155a16",
|
|
71
|
+
"type": "github"
|
|
72
|
+
},
|
|
73
|
+
"original": {
|
|
74
|
+
"owner": "NixOS",
|
|
75
|
+
"ref": "nixpkgs-unstable",
|
|
76
|
+
"repo": "nixpkgs",
|
|
77
|
+
"type": "github"
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"root": {
|
|
81
|
+
"inputs": {
|
|
82
|
+
"android-nixpkgs": "android-nixpkgs",
|
|
83
|
+
"nixpkgs": "nixpkgs"
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"systems": {
|
|
87
|
+
"locked": {
|
|
88
|
+
"lastModified": 1681028828,
|
|
89
|
+
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
|
90
|
+
"owner": "nix-systems",
|
|
91
|
+
"repo": "default",
|
|
92
|
+
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
|
93
|
+
"type": "github"
|
|
94
|
+
},
|
|
95
|
+
"original": {
|
|
96
|
+
"owner": "nix-systems",
|
|
97
|
+
"repo": "default",
|
|
98
|
+
"type": "github"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
"root": "root",
|
|
103
|
+
"version": 7
|
|
104
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
{
|
|
2
|
+
inputs = {
|
|
3
|
+
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
|
4
|
+
|
|
5
|
+
android-nixpkgs = {
|
|
6
|
+
url = "github:tadfisher/android-nixpkgs";
|
|
7
|
+
inputs.nixpkgs.follows = "nixpkgs";
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
outputs =
|
|
12
|
+
{
|
|
13
|
+
self,
|
|
14
|
+
nixpkgs,
|
|
15
|
+
android-nixpkgs,
|
|
16
|
+
}:
|
|
17
|
+
let
|
|
18
|
+
systems = [
|
|
19
|
+
"aarch64-darwin"
|
|
20
|
+
"x86_64-linux"
|
|
21
|
+
];
|
|
22
|
+
forAllSystems = nixpkgs.lib.genAttrs systems;
|
|
23
|
+
|
|
24
|
+
pkgsFor =
|
|
25
|
+
system:
|
|
26
|
+
import nixpkgs {
|
|
27
|
+
inherit system;
|
|
28
|
+
config = {
|
|
29
|
+
allowUnfree = true;
|
|
30
|
+
android_sdk.accept_license = true;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
androidSdkFor =
|
|
35
|
+
system:
|
|
36
|
+
android-nixpkgs.sdk.${system} (
|
|
37
|
+
sdkPkgs: with sdkPkgs; [
|
|
38
|
+
cmdline-tools-latest
|
|
39
|
+
build-tools-35-0-0
|
|
40
|
+
build-tools-36-0-0
|
|
41
|
+
platform-tools
|
|
42
|
+
platforms-android-35
|
|
43
|
+
platforms-android-36
|
|
44
|
+
ndk-27-1-12297006
|
|
45
|
+
ndk-27-0-12077973
|
|
46
|
+
cmake-3-22-1
|
|
47
|
+
]
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
darwinDerivations = {
|
|
51
|
+
xcode-wrapper =
|
|
52
|
+
pkgs:
|
|
53
|
+
pkgs.stdenv.mkDerivation {
|
|
54
|
+
name = "xcode-wrapper-16.4.0";
|
|
55
|
+
buildInputs = [ pkgs.darwin.cctools ];
|
|
56
|
+
buildCommand = ''
|
|
57
|
+
mkdir -p $out/bin
|
|
58
|
+
|
|
59
|
+
cat > $out/bin/xcodebuild << EOF
|
|
60
|
+
#!/bin/sh
|
|
61
|
+
exec /usr/bin/xcodebuild "\$@"
|
|
62
|
+
EOF
|
|
63
|
+
|
|
64
|
+
cat > $out/bin/xcrun << EOF
|
|
65
|
+
#!/bin/sh
|
|
66
|
+
exec /usr/bin/xcrun "\$@"
|
|
67
|
+
EOF
|
|
68
|
+
|
|
69
|
+
cat > $out/bin/xcode-select << EOF
|
|
70
|
+
#!/bin/sh
|
|
71
|
+
if [ "\$1" = "-p" ] && [ -n "\$DEVELOPER_DIR" ]; then
|
|
72
|
+
echo "\$DEVELOPER_DIR"
|
|
73
|
+
else
|
|
74
|
+
exec /usr/bin/xcode-select "\$@"
|
|
75
|
+
fi
|
|
76
|
+
EOF
|
|
77
|
+
|
|
78
|
+
cat > $out/bin/codesign << EOF
|
|
79
|
+
#!/bin/sh
|
|
80
|
+
exec /usr/bin/codesign "\$@"
|
|
81
|
+
EOF
|
|
82
|
+
|
|
83
|
+
cat > $out/bin/ld << EOF
|
|
84
|
+
#!/bin/sh
|
|
85
|
+
exec /usr/bin/ld "\$@"
|
|
86
|
+
EOF
|
|
87
|
+
|
|
88
|
+
cat > $out/bin/clang << EOF
|
|
89
|
+
#!/bin/sh
|
|
90
|
+
exec /usr/bin/clang "\$@"
|
|
91
|
+
EOF
|
|
92
|
+
|
|
93
|
+
chmod +x $out/bin/*
|
|
94
|
+
|
|
95
|
+
if [ -d "/Applications/Xcode.app" ]; then
|
|
96
|
+
DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"
|
|
97
|
+
elif [ -d "/Applications/Xcode-16.4.0.app" ]; then
|
|
98
|
+
DEVELOPER_DIR="/Applications/Xcode-16.4.0.app/Contents/Developer"
|
|
99
|
+
else
|
|
100
|
+
echo "Error: Xcode not found"
|
|
101
|
+
exit 1
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
echo "export DEVELOPER_DIR=\"$DEVELOPER_DIR\"" > $out/bin/env.sh
|
|
105
|
+
'';
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
mkShellFor =
|
|
111
|
+
system:
|
|
112
|
+
let
|
|
113
|
+
pkgs = pkgsFor system;
|
|
114
|
+
androidSdk = androidSdkFor system;
|
|
115
|
+
basePackages = with pkgs; [
|
|
116
|
+
androidSdk
|
|
117
|
+
bun
|
|
118
|
+
just
|
|
119
|
+
];
|
|
120
|
+
|
|
121
|
+
darwinPackages = with pkgs; [
|
|
122
|
+
bundler
|
|
123
|
+
cocoapods
|
|
124
|
+
(darwinDerivations.xcode-wrapper pkgs)
|
|
125
|
+
];
|
|
126
|
+
|
|
127
|
+
darwinHook = ''
|
|
128
|
+
export LC_ALL=en_US.UTF-8
|
|
129
|
+
export LANG=en_US.UTF-8
|
|
130
|
+
export JAVA_HOME="${pkgs.jdk17.home}"
|
|
131
|
+
|
|
132
|
+
unset SDKROOT
|
|
133
|
+
|
|
134
|
+
if [ -f "${darwinDerivations.xcode-wrapper pkgs}/bin/env.sh" ]; then
|
|
135
|
+
source "${darwinDerivations.xcode-wrapper pkgs}/bin/env.sh"
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
export LD=/usr/bin/clang
|
|
139
|
+
export LD_FOR_TARGET=/usr/bin/clang
|
|
140
|
+
|
|
141
|
+
echo "iOS development environment:"
|
|
142
|
+
echo "DEVELOPER_DIR: $DEVELOPER_DIR"
|
|
143
|
+
xcodebuild -version
|
|
144
|
+
'';
|
|
145
|
+
|
|
146
|
+
linuxHook = ''
|
|
147
|
+
export LC_ALL=en_US.UTF-8
|
|
148
|
+
export LANG=en_US.UTF-8
|
|
149
|
+
export JAVA_HOME="${pkgs.jdk17.home}"
|
|
150
|
+
'';
|
|
151
|
+
|
|
152
|
+
in
|
|
153
|
+
pkgs.mkShellNoCC {
|
|
154
|
+
buildInputs = if system == "aarch64-darwin" then basePackages ++ darwinPackages else basePackages;
|
|
155
|
+
|
|
156
|
+
shellHook = if system == "aarch64-darwin" then darwinHook else linuxHook;
|
|
157
|
+
};
|
|
158
|
+
in
|
|
159
|
+
{
|
|
160
|
+
devShells = forAllSystems (system: {
|
|
161
|
+
default = mkShellFor system;
|
|
162
|
+
});
|
|
163
|
+
};
|
|
164
|
+
}
|
package/template/gitignore
CHANGED