Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

npx lint-staged
npx lint-staged --no-stash
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep nvm sourcing for pre-commit hook

Removing the nvm init means npx lint-staged will fail on machines where Node is only available via nvm (common per repo guidance). In that environment, the hook can no longer find npx, so commits are blocked unless users manually export PATH or install a system Node.

Useful? React with 👍 / 👎.

17 changes: 2 additions & 15 deletions .lintstagedrc.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,7 @@
module.exports = {
// Cloud files: use cloud's own eslint and prettier configs
"cloud/**/*.{js,jsx,ts,tsx}": (filenames) => {
return [`eslint --fix --config cloud/eslint.config.mjs ${filenames.join(" ")}`]
},
"cloud/**/*.{js,jsx,ts,tsx,json,md}": (filenames) => {
return [`prettier --write --config cloud/packages/cloud/.prettierrc.js ${filenames.join(" ")}`]
},

// Non-cloud, non-root files: use root eslint and prettier configs
"!(cloud)/**/*.{js,jsx,ts,tsx}": "eslint --fix --quiet",
"!(cloud)/**/*.{js,jsx,ts,tsx,json,css,md,html,yml,yaml}": "prettier --write",

// Root-level config files: prettier only (root eslint needs expo which may not be installed)
"*.{js,jsx,ts,tsx}": "eslint --fix --quiet",
"*.{js,jsx,ts,tsx,json,css,md,html,yml,yaml}": "prettier --write",
"*.json": "prettier --write",
"*.{md,html,yml,yaml}": "prettier --write",

// Swift files
"*.swift": "swiftformat",
}
126 changes: 76 additions & 50 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,27 @@ import tseslint from "typescript-eslint"
import pluginReact from "eslint-plugin-react"
import pluginReactNative from "eslint-plugin-react-native"
import pluginReactotron from "eslint-plugin-reactotron"
import pluginPrettier from "eslint-plugin-prettier"
import prettierConfig from "eslint-config-prettier"
import expoConfig from "eslint-config-expo/flat.js"
import {defineConfig} from "eslint/config"
import { defineConfig } from "eslint/config"

export default defineConfig([
// Recommended configs
// Base configs
expoConfig,
js.configs.recommended,
...tseslint.configs.recommended,
pluginReact.configs.flat.recommended,
pluginReact.configs.flat["jsx-runtime"],
prettierConfig,

// custom config:
// Shared rules (all packages)
{
files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"],
ignores: ["eslint.config.mjs", "metro.config.js"],
plugins: {
"@typescript-eslint": tseslint.plugin,
"react": pluginReact,
"react-native": pluginReactNative,
"reactotron": pluginReactotron,
"prettier": pluginPrettier,
},
languageOptions: {
globals: {
...globals.node,
...globals.browser,
__DEV__: "readonly", // React Native global
},
parser: tseslint.parser,
parserOptions: {
Expand All @@ -42,19 +33,11 @@ export default defineConfig([
},
},
settings: {
"react": {
version: "detect", // Automatically detect the React version
},
"import/resolver": {
typescript: {
project: ["./mobile/tsconfig.json", "./cloud/tsconfig.json"],
},
react: {
version: "detect",
},
},
rules: {
// Prettier
"prettier/prettier": "error",

// TypeScript
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": [
Expand All @@ -73,11 +56,6 @@ export default defineConfig([
// React
"react/prop-types": "off",

// React Native rules are scoped to RN files via an overrides block below

// Reactotron
"reactotron/no-tron-in-production": "error",

// Core ESLint
"prefer-const": "off",
"no-use-before-define": "off",
Expand All @@ -86,29 +64,63 @@ export default defineConfig([
"quotes": "off",
"space-before-function-paren": "off",
"no-case-declarations": "warn",
},
},

// Mobile-specific
{
files: ["mobile/**/*.{js,ts,jsx,tsx}"],
plugins: {
"react-native": pluginReactNative,
"reactotron": pluginReactotron,
},
languageOptions: {
globals: {
__DEV__: "readonly",
},
},
settings: {
"import/resolver": {
typescript: {
project: "./mobile/tsconfig.json",
},
},
},
rules: {
// React Native
"react-native/no-unused-styles": "error",
"react-native/split-platform-components": "warn",
"react-native/no-inline-styles": "warn",
"react-native/no-color-literals": "off",
"react-native/no-raw-text": "error",

// Reactotron
"reactotron/no-tron-in-production": "error",

// Mobile-specific import restrictions
"no-restricted-imports": [
"error",
{
paths: [
{
name: "react",
importNames: ["default"],
message: "Import named exports from 'react' instead / don't import React.",
message: "Import named exports from 'react' instead.",
},
{
name: "react-native",
importNames: ["StyleSheet"],
message: "Do not import StyleSheet from 'react-native'. Use ThemedStyles / the useTheme() hook instead.",
message: "Use ThemedStyles / useTheme() hook instead.",
},
{
name: "expo-router",
importNames: ["useRouter"],
message: "Do not use useRouter from expo-router. Use our useNavigationHistory hook instead.",
message: "Use useNavigationHistory hook instead.",
},
{
name: "react-native",
importNames: ["Text"],
message: "Do not import Text from 'react-native'. Use the Ignite component with the tx prop instead.",
message: "Use the Ignite Text component with tx prop instead.",
},
],
patterns: [
Expand All @@ -119,33 +131,47 @@ export default defineConfig([
],
},
],
// Disabled: import/order causes mass file changes across PRs
// "import/order": "off",
},
},

// Jest setup files configuration
// Cloud-specific
{
files: ["**/jest.setup.js", "**/jest.config.js", "**/*.test.{js,ts,jsx,tsx}", "**/*.spec.{js,ts,jsx,tsx}"],
languageOptions: {
globals: {
...globals.jest,
files: ["cloud/**/*.{js,ts,jsx,tsx}"],
settings: {
"import/resolver": {
typescript: {
project: "./cloud/tsconfig.json",
},
},
},
rules: {
// Add cloud-specific rules here
"no-restricted-imports": [
"error",
{
patterns: [
{
group: ["../*"],
message: "Use @/ path aliases instead of relative imports",
},
],
},
],
},
},

// React Native-only rules (scoped override)
// NOTE: eslint-plugin-react-native rules were being applied to web code (e.g., the console),
// triggering RN-only checks like react-native/no-raw-text in regular React. The plugin does not
// auto-detect platform, so we explicitly scope these rules to RN files and paths.
// Test files
{
files: ["mobile/**/*.{js,ts,jsx,tsx}", "**/*.native.{js,ts,jsx,tsx}"],
rules: {
"react-native/no-unused-styles": "error",
"react-native/split-platform-components": "warn",
"react-native/no-inline-styles": "warn",
"react-native/no-color-literals": "off",
"react-native/no-raw-text": "error",
files: [
"**/jest.setup.js",
"**/jest.config.js",
"**/*.test.{js,ts,jsx,tsx}",
"**/*.spec.{js,ts,jsx,tsx}",
],
languageOptions: {
globals: {
...globals.jest,
},
},
},

Expand Down Expand Up @@ -173,4 +199,4 @@ export default defineConfig([
"mobile/**/*.aab",
],
},
])
])
1 change: 1 addition & 0 deletions mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"android:flash:prebuilt": "zx ./scripts/android-flash-prebuilt.mjs",
"build:google:play": "zx ./scripts/build-google-play.mjs",
"upload:google:play": "zx ./scripts/upload-google-play.mjs",
"check:deps": "zx ./scripts/check-deps.mjs",
"compile": "tsc --noEmit -p . --pretty",
"lint": "eslint . --fix",
"lint:check": "eslint .",
Expand Down
10 changes: 5 additions & 5 deletions mobile/scripts/android-flash-prebuilt.mjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#!/usr/bin/env zx

import { setBuildEnv } from './set-build-env.mjs';
await setBuildEnv();
import setBuildEnv from "./set-build-env.mjs"
await setBuildEnv()

console.log('Flashing last built Android release...');
console.log("Flashing last built Android release...")

// Install APK on device
await $({ stdio: 'inherit' })`adb install -r android/app/build/outputs/apk/release/app-release.apk`;
await $({ stdio: "inherit" })`adb install -r android/app/build/outputs/apk/release/app-release.apk`

console.log('✅ Android release installed successfully!');
console.log("✅ Android release installed successfully!")
14 changes: 7 additions & 7 deletions mobile/scripts/android-release.mjs
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
#!/usr/bin/env zx

import { setBuildEnv } from './set-build-env.mjs';
await setBuildEnv();
import setBuildEnv from "./set-build-env.mjs"
await setBuildEnv()

console.log('Building Android release...');
console.log("Building Android release...")

// Prebuild Android
await $({ stdio: 'inherit' })`bun expo prebuild --platform android`;
await $({ stdio: "inherit" })`bun expo prebuild --platform android`

// bundle js code:
await $({stdio: "inherit"})`bun expo export --platform android`

// Build release APK
await $({ stdio: 'inherit', cwd: 'android' })`./gradlew assembleRelease`;
await $({ stdio: "inherit", cwd: "android" })`./gradlew assembleRelease`

// Install APK on device
await $({ stdio: 'inherit' })`adb install -r android/app/build/outputs/apk/release/app-release.apk`;
await $({ stdio: "inherit" })`adb install -r android/app/build/outputs/apk/release/app-release.apk`

console.log('✅ Android release built and installed successfully!');
console.log("✅ Android release built and installed successfully!")
2 changes: 1 addition & 1 deletion mobile/scripts/android.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env zx
import {setBuildEnv} from "./set-build-env.mjs"
import setBuildEnv from "./set-build-env.mjs"
await setBuildEnv()

// prebuild android:
Expand Down
16 changes: 8 additions & 8 deletions mobile/scripts/build-google-play.mjs
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
#!/usr/bin/env zx

import { setBuildEnv } from './set-build-env.mjs';
await setBuildEnv();
import setBuildEnv from "./set-build-env.mjs"
await setBuildEnv()

console.log('Building AAB for Google Play...');
console.log("Building AAB for Google Play...")

// Prebuild Android
await $({ stdio: 'inherit' })`bun expo prebuild --platform android`;
await $({ stdio: "inherit" })`bun expo prebuild --platform android`

// Bundle JS code
await $({ stdio: 'inherit' })`bun expo export --platform android`;
await $({ stdio: "inherit" })`bun expo export --platform android`

// Build release AAB
await $({ stdio: 'inherit', cwd: 'android' })`./gradlew bundleRelease`;
await $({ stdio: "inherit", cwd: "android" })`./gradlew bundleRelease`

console.log('✅ AAB built successfully!');
console.log('📦 Output: android/app/build/outputs/bundle/release/app-release.aab');
console.log("✅ AAB built successfully!")
console.log("📦 Output: android/app/build/outputs/bundle/release/app-release.aab")
28 changes: 28 additions & 0 deletions mobile/scripts/check-deps.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
async function checkDeps() {
console.log("Checking dependencies...")

// check if nvm's script exists:
const nvmDir = process.env.NVM_DIR || `${process.env.HOME}/.nvm`
if (!fs.existsSync(`${nvmDir}/nvm.sh`)) {
console.warn("⚠️ nvm not found...")
Comment on lines +5 to +7
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Import fs before use in check-deps

The new check-deps.mjs references fs.existsSync(...) but never imports fs, so running bun run postinstall (which now calls checkDeps()) will throw a ReferenceError: fs is not defined and abort the install flow. This breaks dependency setup for anyone running the mobile postinstall script.

Useful? React with 👍 / 👎.

}

// Check if swiftformat is installed
try {
await $`command -v swiftformat`
} catch {
console.warn(`
⚠️ swiftformat not found.

Install it with Homebrew:

brew install swiftformat
`)
}
}

export default checkDeps

if (import.meta.url === `file://${process.argv[1]}`) {
await checkDeps()
}
2 changes: 1 addition & 1 deletion mobile/scripts/ios-release.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env zx
import {setBuildEnv} from "./set-build-env.mjs"
import setBuildEnv from "./set-build-env.mjs"
await setBuildEnv()

// Build iOS archive
Expand Down
6 changes: 3 additions & 3 deletions mobile/scripts/ios.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env zx
import { setBuildEnv } from './set-build-env.mjs';
await setBuildEnv();
import setBuildEnv from "./set-build-env.mjs"
await setBuildEnv()

// prebuild ios:
await $({stdio: "inherit"})`bun expo prebuild --platform ios`
Expand All @@ -9,4 +9,4 @@ await $({stdio: "inherit"})`bun expo prebuild --platform ios`
await $({stdio: "inherit"})`cp .env ios/.xcode.env.local`

// Run expo iOS command with stdin enabled for interactive prompts
await $({ stdio: 'inherit' })`bun expo run:ios --device`;
await $({stdio: "inherit"})`bun expo run:ios --device`
Loading
Loading