feat: add tui app
This commit is contained in:
@@ -1,130 +1,14 @@
|
|||||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
import { authClient } from '@/lib/auth-client';
|
|
||||||
import { Button, Linking, Platform, Pressable, Text, View } from 'react-native';
|
|
||||||
import { useQuery, useZero } from "@rocicorp/zero/react";
|
|
||||||
import { queries, type Mutators, type Schema } from '@money/shared';
|
|
||||||
import Header from '@/components/Header';
|
import Header from '@/components/Header';
|
||||||
import { useEffect, useState, type ReactNode } from 'react';
|
|
||||||
|
import { Settings } from "@money/ui";
|
||||||
|
|
||||||
export default function HomeScreen() {
|
export default function HomeScreen() {
|
||||||
const { data: session } = authClient.useSession();
|
|
||||||
|
|
||||||
const onLogout = () => {
|
|
||||||
authClient.signOut();
|
|
||||||
}
|
|
||||||
const z = useZero<Schema, Mutators>();
|
|
||||||
const [plaidLink] = useQuery(queries.getPlaidLink(session));
|
|
||||||
const [items] = useQuery(queries.getItems(session));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView>
|
<SafeAreaView>
|
||||||
<Header />
|
<Header />
|
||||||
|
<Settings />
|
||||||
<UI
|
|
||||||
columns={[
|
|
||||||
{
|
|
||||||
name: "Banks",
|
|
||||||
items: items,
|
|
||||||
renderItem: (item, props) => <Row {...props}>{item.name}</Row>
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Family",
|
|
||||||
items: [],
|
|
||||||
renderItem() {
|
|
||||||
return <View></View>;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
type Col<T> = {
|
|
||||||
name: string;
|
|
||||||
items: T[];
|
|
||||||
renderItem: (item: T, props: { isSelected: boolean, isActive: boolean }) => ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
type State = {
|
|
||||||
idx: number;
|
|
||||||
columns: Map<number, State>;
|
|
||||||
};
|
|
||||||
|
|
||||||
function UI({ columns }: { columns: Col<any>[] }) {
|
|
||||||
const [col, setCol] = useState(0);
|
|
||||||
const [state, setState] = useState<State>({
|
|
||||||
idx: 0,
|
|
||||||
columns: new Map(
|
|
||||||
Array.from({ length: columns.length })
|
|
||||||
.map((_, i) => ([i, { idx: 0, columns: new Map() }]))
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
const getColState = (res: State): State => {
|
|
||||||
let i = col;
|
|
||||||
while (i > 0) {
|
|
||||||
res = res.columns.get(res.idx)!;
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const colState = getColState(state);
|
|
||||||
|
|
||||||
const curr = columns.at(col)!;
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (Platform.OS != 'web') return;
|
|
||||||
const handleKeyDown = (event: KeyboardEvent) => {
|
|
||||||
if (event.key === "j") {
|
|
||||||
setState((prev) => {
|
|
||||||
if (prev.idx + 1 == colState.columns.size) return prev;
|
|
||||||
return {...prev, ...{ idx: prev.idx + 1 }};
|
|
||||||
});
|
|
||||||
} else if (event.key === "k") {
|
|
||||||
setState((prev) => {
|
|
||||||
if (prev.idx == 0) return prev;
|
|
||||||
return {...prev, ...{ idx: prev.idx - 1 }};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener("keydown", handleKeyDown);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener("keydown", handleKeyDown);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View style={{ flexDirection: "row" }}>
|
|
||||||
<Column>
|
|
||||||
{columns.map((c, i) => <Pressable onPress={() => { setCol(i) }}><Row isSelected={col == i} isActive={col == 0}>{c.name}</Row></Pressable>)}
|
|
||||||
</Column>
|
|
||||||
<Column>
|
|
||||||
{curr.items.map((item, i) => curr.renderItem(item, { isSelected: colState.idx == i, isActive: col == 1 }))}
|
|
||||||
</Column>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Column({ children }: { children: ReactNode }) {
|
|
||||||
return (
|
|
||||||
<View>
|
|
||||||
{children}
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Row({ children, isSelected, isActive }: { children: ReactNode, isSelected: boolean, isActive: boolean }) {
|
|
||||||
const color = isSelected ? 'white': undefined;
|
|
||||||
const backgroundColor = isSelected ? (isActive ? 'black' : 'gray'): undefined;
|
|
||||||
return (
|
|
||||||
<View>
|
|
||||||
<Text style={{ fontFamily: 'mono', color, backgroundColor }}>{children}</Text>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
"@better-auth/expo": "^1.3.27",
|
"@better-auth/expo": "^1.3.27",
|
||||||
"@expo/vector-icons": "^15.0.2",
|
"@expo/vector-icons": "^15.0.2",
|
||||||
"@money/shared": "workspace:*",
|
"@money/shared": "workspace:*",
|
||||||
|
"@money/ui": "workspace:*",
|
||||||
"@react-navigation/bottom-tabs": "^7.4.0",
|
"@react-navigation/bottom-tabs": "^7.4.0",
|
||||||
"@react-navigation/elements": "^2.6.3",
|
"@react-navigation/elements": "^2.6.3",
|
||||||
"@react-navigation/native": "^7.1.8",
|
"@react-navigation/native": "^7.1.8",
|
||||||
|
|||||||
34
apps/tui/.gitignore
vendored
Normal file
34
apps/tui/.gitignore
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# dependencies (bun install)
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# output
|
||||||
|
out
|
||||||
|
dist
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# code coverage
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# caches
|
||||||
|
.eslintcache
|
||||||
|
.cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# IntelliJ based IDEs
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Finder (MacOS) folder config
|
||||||
|
.DS_Store
|
||||||
15
apps/tui/README.md
Normal file
15
apps/tui/README.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# react
|
||||||
|
|
||||||
|
To install dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
|
||||||
|
To run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run src/index.tsx
|
||||||
|
```
|
||||||
|
|
||||||
|
This project was created using `bun create tui`. [create-tui](https://git.new/create-tui) is the easiest way to get started with OpenTUI.
|
||||||
41
apps/tui/build.ts
Normal file
41
apps/tui/build.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import esbuild from "esbuild";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
// Custom plugin to alias "react-native" to react-native-opentui
|
||||||
|
const aliasPlugin = {
|
||||||
|
name: "alias-react-native",
|
||||||
|
setup(build) {
|
||||||
|
build.onResolve({ filter: /^react-native$/ }, args => {
|
||||||
|
return {
|
||||||
|
path: path.resolve(__dirname, "../../packages/react-native-opentui/index.tsx"),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Build configuration
|
||||||
|
await esbuild.build({
|
||||||
|
entryPoints: ["src/index.tsx"], // your app entry
|
||||||
|
bundle: true, // inline all dependencies (ui included)
|
||||||
|
platform: "node", // Node/Bun target
|
||||||
|
format: "esm", // keep ESM for top-level await
|
||||||
|
outfile: "dist/index.js",
|
||||||
|
sourcemap: true,
|
||||||
|
plugins: [aliasPlugin],
|
||||||
|
loader: {
|
||||||
|
".ts": "ts",
|
||||||
|
".tsx": "tsx",
|
||||||
|
},
|
||||||
|
external: [
|
||||||
|
// leave OpenTUI and Bun built-ins for Bun runtime
|
||||||
|
"react",
|
||||||
|
"@opentui/core",
|
||||||
|
"@opentui/react",
|
||||||
|
"@opentui/react/jsx-runtime",
|
||||||
|
"bun:ffi",
|
||||||
|
// "./assets/**/*.scm",
|
||||||
|
// "./assets/**/*.wasm",
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("✅ App bundled successfully");
|
||||||
22
apps/tui/package.json
Normal file
22
apps/tui/package.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "@money/tui",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"scripts": {
|
||||||
|
"build": "bun run build.js",
|
||||||
|
"start": "bun run dist/index.js"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "*"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@money/ui": "workspace:*",
|
||||||
|
"@money/shared": "workspace:*",
|
||||||
|
"@opentui/core": "^0.1.39",
|
||||||
|
"@opentui/react": "^0.1.39",
|
||||||
|
"react-native": "^0.82.1",
|
||||||
|
"react-native-opentui": "workspace:*"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"esbuild": "^0.27.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
20
apps/tui/src/index.tsx
Normal file
20
apps/tui/src/index.tsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { RGBA, TextAttributes, createCliRenderer } from "@opentui/core";
|
||||||
|
import { createRoot } from "@opentui/react";
|
||||||
|
import { Settings } from "@money/ui";
|
||||||
|
import { ZeroProvider } from "@rocicorp/zero/react";
|
||||||
|
import { schema } from '@money/shared';
|
||||||
|
|
||||||
|
const userID = "anon";
|
||||||
|
const server = "http://laptop:4848";
|
||||||
|
const auth = undefined;
|
||||||
|
|
||||||
|
function Main() {
|
||||||
|
return (
|
||||||
|
<ZeroProvider {...{ userID, auth, server, schema }}>
|
||||||
|
<Settings />
|
||||||
|
</ZeroProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderer = await createCliRenderer();
|
||||||
|
createRoot(renderer).render(<Main />);
|
||||||
33
apps/tui/tsconfig.json
Normal file
33
apps/tui/tsconfig.json
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"paths": {
|
||||||
|
"react-native": ["../react-native-opentui"]
|
||||||
|
},
|
||||||
|
// Environment setup & latest features
|
||||||
|
"lib": ["ESNext"],
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "Preserve",
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"jsxImportSource": "@opentui/react",
|
||||||
|
"allowJs": true,
|
||||||
|
|
||||||
|
// Bundler mode
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
// Best practices
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
|
||||||
|
// Some stricter flags (disabled by default)
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"noPropertyAccessFromIndexSignature": false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
packages = with pkgs; [
|
packages = with pkgs; [
|
||||||
corepack
|
corepack
|
||||||
nodejs_22
|
nodejs_22
|
||||||
|
bun
|
||||||
|
|
||||||
postgresql
|
postgresql
|
||||||
process-compose
|
process-compose
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
"name": "money",
|
"name": "money",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "process-compose up -p 0"
|
"dev": "process-compose up -p 0",
|
||||||
|
"tui": "bun run --hot apps/tui/src/index.tsx"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"onlyBuiltDependencies": [
|
"onlyBuiltDependencies": [
|
||||||
|
|||||||
33
packages/react-native-opentui/index.tsx
Normal file
33
packages/react-native-opentui/index.tsx
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import type { ViewProps, TextProps } from "react-native";
|
||||||
|
|
||||||
|
export function View({ children, style }: ViewProps) {
|
||||||
|
const bg = style &&
|
||||||
|
'backgroundColor' in style
|
||||||
|
? typeof style.backgroundColor == 'string'
|
||||||
|
? style.backgroundColor
|
||||||
|
: undefined
|
||||||
|
: undefined;
|
||||||
|
return <box backgroundColor={bg}>{children}</box>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Text({ style, children }: TextProps) {
|
||||||
|
const fg = style &&
|
||||||
|
'color' in style
|
||||||
|
? typeof style.color == 'string'
|
||||||
|
? style.color
|
||||||
|
: undefined
|
||||||
|
: undefined;
|
||||||
|
return <text fg={fg || "black"}>{children}</text>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Platform = {
|
||||||
|
OS: "tui",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
View,
|
||||||
|
Text,
|
||||||
|
}
|
||||||
11
packages/react-native-opentui/package.json
Normal file
11
packages/react-native-opentui/package.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "react-native-opentui",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.tsx",
|
||||||
|
"exports": {
|
||||||
|
".": "./index.tsx"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
12
packages/react-native-opentui/tsconfig.json
Normal file
12
packages/react-native-opentui/tsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"lib": ["ESNext", "DOM"],
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"jsxImportSource": "@opentui/react",
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,9 +32,9 @@ export const queries = {
|
|||||||
.orderBy('name', 'asc');
|
.orderBy('name', 'asc');
|
||||||
}),
|
}),
|
||||||
getItems: syncedQueryWithContext('getItems', z.tuple([]), (authData: AuthData | null) => {
|
getItems: syncedQueryWithContext('getItems', z.tuple([]), (authData: AuthData | null) => {
|
||||||
isLoggedIn(authData);
|
// isLoggedIn(authData);
|
||||||
return builder.plaidAccessTokens
|
return builder.plaidAccessTokens
|
||||||
.where('userId', '=', authData.user.id)
|
// .where('userId', '=', authData.user.id)
|
||||||
.orderBy('createdAt', 'desc');
|
.orderBy('createdAt', 'desc');
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|||||||
14
packages/ui/package.json
Normal file
14
packages/ui/package.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "@money/ui",
|
||||||
|
"private": true,
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.tsx"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"react-native-opentui": "workspace:*",
|
||||||
|
"@money/shared": "workspace:*"
|
||||||
|
},
|
||||||
|
"packageManager": "pnpm@10.18.2"
|
||||||
|
}
|
||||||
15
packages/ui/src/index.tsx
Normal file
15
packages/ui/src/index.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { Text } from "react-native";
|
||||||
|
import { List } from "./list";
|
||||||
|
import { useQuery } from "@rocicorp/zero/react";
|
||||||
|
import { queries } from '@money/shared';
|
||||||
|
|
||||||
|
export function Settings() {
|
||||||
|
const [items] = useQuery(queries.getItems(null));
|
||||||
|
|
||||||
|
return <List
|
||||||
|
items={items}
|
||||||
|
renderItem={({ item, isSelected }) => <Text style={{ color: isSelected ? 'white' : 'black' }}>{item.name}</Text>}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
30
packages/ui/src/list.tsx
Normal file
30
packages/ui/src/list.tsx
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { useState, type ReactNode } from "react";
|
||||||
|
import { View, Text } from "react-native";
|
||||||
|
import { useKeyboard } from "./useKeyboard";
|
||||||
|
|
||||||
|
export type ListProps<T> = {
|
||||||
|
items: T[],
|
||||||
|
renderItem: (props: { item: T, isSelected: boolean }) => ReactNode;
|
||||||
|
};
|
||||||
|
export function List<T>({ items, renderItem }: ListProps<T>) {
|
||||||
|
const [idx, setIdx] = useState(0);
|
||||||
|
|
||||||
|
useKeyboard((key) => {
|
||||||
|
if (key.name == 'j') {
|
||||||
|
setIdx((prevIdx) => prevIdx + 1 < items.length ? prevIdx + 1 : items.length - 1);
|
||||||
|
} else if (key.name == 'k') {
|
||||||
|
setIdx((prevIdx) => prevIdx == 0 ? 0 : prevIdx - 1);
|
||||||
|
} else if (key.name == 'g' && key.shift) {
|
||||||
|
setIdx(items.length - 1);
|
||||||
|
}
|
||||||
|
}, [items]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
{items.map((item, index) => <View style={{ backgroundColor: index == idx ? 'black' : undefined }}>
|
||||||
|
{renderItem({ item, isSelected: index == idx })}
|
||||||
|
</View>)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
5
packages/ui/src/useKeyboard.ts
Normal file
5
packages/ui/src/useKeyboard.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { useKeyboard as useOpentuiKeyboard } from "@opentui/react";
|
||||||
|
|
||||||
|
export function useKeyboard(handler: Parameters<typeof useOpentuiKeyboard>[0], _deps: any[] = []) {
|
||||||
|
return useOpentuiKeyboard(handler);
|
||||||
|
}
|
||||||
35
packages/ui/src/useKeyboard.web.ts
Normal file
35
packages/ui/src/useKeyboard.web.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { useEffect } from "react";
|
||||||
|
import type { KeyboardEvent } from "react";
|
||||||
|
import type { KeyEvent } from "@opentui/core";
|
||||||
|
|
||||||
|
|
||||||
|
export function useKeyboard(handler: (key: KeyEvent) => void, deps: any[] = []) {
|
||||||
|
useEffect(() => {
|
||||||
|
const handlerWeb = (event: KeyboardEvent) => {
|
||||||
|
// @ts-ignore
|
||||||
|
handler({
|
||||||
|
name: event.key.toLowerCase(),
|
||||||
|
ctrl: event.ctrlKey,
|
||||||
|
meta: event.metaKey,
|
||||||
|
shift: event.shiftKey,
|
||||||
|
option: event.metaKey,
|
||||||
|
sequence: '',
|
||||||
|
number: false,
|
||||||
|
raw: '',
|
||||||
|
eventType: 'press',
|
||||||
|
source: "raw",
|
||||||
|
code: event.code,
|
||||||
|
super: false,
|
||||||
|
hyper: false,
|
||||||
|
capsLock: false,
|
||||||
|
numLock: false,
|
||||||
|
baseCode: event.keyCode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
window.addEventListener("keydown", handlerWeb);
|
||||||
|
// @ts-ignore
|
||||||
|
return () => window.removeEventListener("keydown", handlerWeb);
|
||||||
|
}, deps);
|
||||||
|
}
|
||||||
28
packages/ui/tsconfig.json
Normal file
28
packages/ui/tsconfig.json
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
// Environment setup & latest features
|
||||||
|
"lib": ["ESNext"],
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"allowJs": true,
|
||||||
|
|
||||||
|
// Bundler mode
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
// Best practices
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
|
||||||
|
// Some stricter flags (disabled by default)
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"noPropertyAccessFromIndexSignature": false
|
||||||
|
}
|
||||||
|
}
|
||||||
2042
pnpm-lock.yaml
generated
2042
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -55,7 +55,7 @@ processes:
|
|||||||
|
|
||||||
studio:
|
studio:
|
||||||
command: npx drizzle-kit studio
|
command: npx drizzle-kit studio
|
||||||
working_dir: ./shared
|
working_dir: ./packages/shared
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: process_healthy
|
condition: process_healthy
|
||||||
|
|||||||
Reference in New Issue
Block a user