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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-react-native-airborne",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Scaffold a react-native-airborne starter project",
5
5
  "repository": {
6
6
  "type": "git",
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("Usage: bun create react-native-airborne <project-name> [--skip-install] [--no-git]");
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");
@@ -0,0 +1 @@
1
+ use flake . --no-pure-eval
@@ -19,7 +19,7 @@ jobs:
19
19
  bun-version: "1.3.4"
20
20
 
21
21
  - name: "📦 Install dependencies"
22
- run: bun install --workspaces
22
+ run: bun install --workspaces --frozen-lockfile
23
23
 
24
24
  - name: "🔍 Lint"
25
25
  run: |
@@ -1,6 +1,8 @@
1
1
  # React Native Airborne
2
2
 
3
- Opinionated React Native starter for mobile-first apps with Expo + Convex.
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
- ## 🤝 Contributor Guide
18
+ ## 🎯 Opinionated Defaults
16
19
 
17
- Detailed implementation and maintenance notes for engineers/agents live in `AGENTS.md`.
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 + Convex project
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
- Then run both apps:
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/type lint checks
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
- ### Client (`client/.env`)
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
- ### Server (`server/.env`)
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
- After connecting Convex, run `cd server && bun run codegen` to regenerate.
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
+ }
@@ -25,3 +25,4 @@ coverage/
25
25
  # macOS
26
26
  .DS_Store
27
27
  .npmrc
28
+ .direnv/
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "__APP_NAME__",
3
- "version": "0.1.1",
3
+ "version": "0.1.0",
4
4
  "private": true,
5
5
  "packageManager": "bun@1.3.4",
6
6
  "workspaces": [