@coze-arch/cli 0.0.1-alpha.b0f2c2 → 0.0.1-alpha.b287c1
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 +1 -0
- package/lib/__templates__/expo/.cozeproj/scripts/dev_run.sh +13 -12
- package/lib/__templates__/expo/.cozeproj/scripts/server_dev_run.sh +9 -8
- package/lib/__templates__/expo/AGENTS.md +74 -0
- package/lib/__templates__/expo/README.md +0 -73
- package/lib/__templates__/expo/_npmrc +1 -0
- package/lib/__templates__/expo/client/components/Screen.tsx +2 -2
- package/lib/__templates__/expo/client/eslint.config.mjs +7 -0
- package/lib/__templates__/expo/client/metro.config.js +3 -0
- package/lib/__templates__/expo/client/package.json +35 -35
- package/lib/__templates__/expo/client/screens/demo/index.tsx +3 -3
- package/lib/__templates__/expo/client/scripts/install-missing-deps.js +10 -10
- package/lib/__templates__/expo/eslint-plugins/forbid-emoji/index.js +9 -0
- package/lib/__templates__/expo/eslint-plugins/forbid-emoji/rule.js +112 -0
- package/lib/__templates__/expo/eslint-plugins/forbid-emoji/tech.md +94 -0
- package/lib/__templates__/expo/eslint-plugins/restrict-linear-gradient/index.js +9 -0
- package/lib/__templates__/expo/eslint-plugins/restrict-linear-gradient/rule.js +120 -0
- package/lib/__templates__/expo/eslint-plugins/restrict-linear-gradient/tech.md +58 -0
- package/lib/__templates__/expo/package.json +1 -1
- package/lib/__templates__/expo/patches/{expo@54.0.32.patch → expo@54.0.33.patch} +3 -2
- package/lib/__templates__/expo/pnpm-lock.yaml +340 -1736
- package/lib/__templates__/expo/server/package.json +9 -7
- package/lib/__templates__/expo/server/src/index.ts +1 -0
- package/lib/__templates__/expo/template.config.js +56 -0
- package/lib/__templates__/native-static/.coze +11 -0
- package/lib/__templates__/native-static/index.html +33 -0
- package/lib/__templates__/native-static/styles/main.css +136 -0
- package/lib/__templates__/native-static/template.config.js +22 -0
- package/lib/__templates__/nextjs/AGENTS.md +54 -0
- package/lib/__templates__/nextjs/README.md +5 -0
- package/lib/__templates__/nextjs/eslint.config.mjs +5 -0
- package/lib/__templates__/nextjs/next.config.ts +1 -2
- package/lib/__templates__/nextjs/package.json +5 -6
- package/lib/__templates__/nextjs/pnpm-lock.yaml +1145 -109
- package/lib/__templates__/nextjs/scripts/build.sh +4 -1
- package/lib/__templates__/nextjs/scripts/dev.sh +8 -2
- package/lib/__templates__/nextjs/scripts/start.sh +7 -1
- package/lib/__templates__/nextjs/src/app/layout.tsx +1 -1
- package/lib/__templates__/nextjs/src/app/page.tsx +17 -60
- package/lib/__templates__/nextjs/src/server.ts +35 -0
- package/lib/__templates__/nextjs/template.config.js +49 -14
- package/lib/__templates__/nextjs/tsconfig.json +1 -1
- package/lib/__templates__/nuxt-vue/.coze +12 -0
- package/lib/__templates__/nuxt-vue/AGENTS.md +42 -0
- package/lib/__templates__/nuxt-vue/README.md +73 -0
- package/lib/__templates__/nuxt-vue/_gitignore +24 -0
- package/lib/__templates__/nuxt-vue/_npmrc +23 -0
- package/lib/__templates__/nuxt-vue/app/app.vue +6 -0
- package/lib/__templates__/nuxt-vue/app/pages/index.vue +23 -0
- package/lib/__templates__/nuxt-vue/assets/css/main.css +24 -0
- package/lib/__templates__/nuxt-vue/nuxt.config.ts +116 -0
- package/lib/__templates__/nuxt-vue/package.json +35 -0
- package/lib/__templates__/nuxt-vue/pnpm-lock.yaml +8759 -0
- package/lib/__templates__/nuxt-vue/postcss.config.mjs +8 -0
- package/lib/__templates__/nuxt-vue/public/favicon.ico +0 -0
- package/lib/__templates__/nuxt-vue/public/robots.txt +2 -0
- package/lib/__templates__/nuxt-vue/scripts/build.sh +14 -0
- package/lib/__templates__/nuxt-vue/scripts/dev.sh +39 -0
- package/lib/__templates__/nuxt-vue/scripts/prepare.sh +14 -0
- package/lib/__templates__/nuxt-vue/scripts/start.sh +21 -0
- package/lib/__templates__/nuxt-vue/server/api/hello.ts +10 -0
- package/lib/__templates__/nuxt-vue/server/middleware/logger.ts +10 -0
- package/lib/__templates__/nuxt-vue/server/routes/health.ts +10 -0
- package/lib/__templates__/nuxt-vue/tailwind.config.js +13 -0
- package/lib/__templates__/nuxt-vue/template.config.js +87 -0
- package/lib/__templates__/nuxt-vue/tsconfig.json +18 -0
- package/lib/__templates__/taro/.coze +1 -1
- package/lib/__templates__/taro/.cozeproj/scripts/deploy_build.sh +2 -2
- package/lib/__templates__/taro/.cozeproj/scripts/deploy_run.sh +4 -3
- package/lib/__templates__/taro/.cozeproj/scripts/dev_build.sh +0 -15
- package/lib/__templates__/taro/.cozeproj/scripts/dev_run.sh +117 -24
- package/lib/__templates__/taro/.cozeproj/scripts/pack.sh +24 -1
- package/lib/__templates__/taro/README.md +138 -62
- package/lib/__templates__/taro/config/index.ts +106 -41
- package/lib/__templates__/taro/config/prod.ts +4 -5
- package/lib/__templates__/taro/eslint.config.mjs +82 -4
- package/lib/__templates__/taro/package.json +22 -13
- package/lib/__templates__/taro/patches/@tarojs__plugin-mini-ci@4.1.9.patch +30 -0
- package/lib/__templates__/taro/pnpm-lock.yaml +1307 -423
- package/lib/__templates__/taro/server/package.json +3 -1
- package/lib/__templates__/taro/server/src/main.ts +14 -2
- package/lib/__templates__/taro/src/app.css +141 -37
- package/lib/__templates__/taro/src/app.tsx +9 -0
- package/lib/__templates__/taro/src/components/ui/accordion.tsx +159 -0
- package/lib/__templates__/taro/src/components/ui/alert-dialog.tsx +260 -0
- package/lib/__templates__/taro/src/components/ui/alert.tsx +60 -0
- package/lib/__templates__/taro/src/components/ui/aspect-ratio.tsx +36 -0
- package/lib/__templates__/taro/src/components/ui/avatar.tsx +84 -0
- package/lib/__templates__/taro/src/components/ui/badge.tsx +37 -0
- package/lib/__templates__/taro/src/components/ui/breadcrumb.tsx +117 -0
- package/lib/__templates__/taro/src/components/ui/button-group.tsx +83 -0
- package/lib/__templates__/taro/src/components/ui/button.tsx +67 -0
- package/lib/__templates__/taro/src/components/ui/calendar.tsx +394 -0
- package/lib/__templates__/taro/src/components/ui/card.tsx +108 -0
- package/lib/__templates__/taro/src/components/ui/carousel.tsx +228 -0
- package/lib/__templates__/taro/src/components/ui/checkbox.tsx +58 -0
- package/lib/__templates__/taro/src/components/ui/code-block.tsx +169 -0
- package/lib/__templates__/taro/src/components/ui/collapsible.tsx +71 -0
- package/lib/__templates__/taro/src/components/ui/command.tsx +385 -0
- package/lib/__templates__/taro/src/components/ui/context-menu.tsx +614 -0
- package/lib/__templates__/taro/src/components/ui/dialog.tsx +256 -0
- package/lib/__templates__/taro/src/components/ui/drawer.tsx +192 -0
- package/lib/__templates__/taro/src/components/ui/dropdown-menu.tsx +561 -0
- package/lib/__templates__/taro/src/components/ui/field.tsx +228 -0
- package/lib/__templates__/taro/src/components/ui/hover-card.tsx +282 -0
- package/lib/__templates__/taro/src/components/ui/input-group.tsx +197 -0
- package/lib/__templates__/taro/src/components/ui/input-otp.tsx +136 -0
- package/lib/__templates__/taro/src/components/ui/input.tsx +56 -0
- package/lib/__templates__/taro/src/components/ui/label.tsx +24 -0
- package/lib/__templates__/taro/src/components/ui/menubar.tsx +595 -0
- package/lib/__templates__/taro/src/components/ui/navigation-menu.tsx +264 -0
- package/lib/__templates__/taro/src/components/ui/pagination.tsx +118 -0
- package/lib/__templates__/taro/src/components/ui/popover.tsx +291 -0
- package/lib/__templates__/taro/src/components/ui/portal.tsx +19 -0
- package/lib/__templates__/taro/src/components/ui/progress.tsx +28 -0
- package/lib/__templates__/taro/src/components/ui/radio-group.tsx +64 -0
- package/lib/__templates__/taro/src/components/ui/resizable.tsx +346 -0
- package/lib/__templates__/taro/src/components/ui/scroll-area.tsx +34 -0
- package/lib/__templates__/taro/src/components/ui/select.tsx +438 -0
- package/lib/__templates__/taro/src/components/ui/separator.tsx +30 -0
- package/lib/__templates__/taro/src/components/ui/sheet.tsx +262 -0
- package/lib/__templates__/taro/src/components/ui/skeleton.tsx +17 -0
- package/lib/__templates__/taro/src/components/ui/slider.tsx +203 -0
- package/lib/__templates__/taro/src/components/ui/sonner.tsx +1 -0
- package/lib/__templates__/taro/src/components/ui/switch.tsx +55 -0
- package/lib/__templates__/taro/src/components/ui/table.tsx +142 -0
- package/lib/__templates__/taro/src/components/ui/tabs.tsx +114 -0
- package/lib/__templates__/taro/src/components/ui/textarea.tsx +54 -0
- package/lib/__templates__/taro/src/components/ui/toast.tsx +517 -0
- package/lib/__templates__/taro/src/components/ui/toggle-group.tsx +120 -0
- package/lib/__templates__/taro/src/components/ui/toggle.tsx +77 -0
- package/lib/__templates__/taro/src/components/ui/tooltip.tsx +455 -0
- package/lib/__templates__/taro/src/index.html +20 -1
- package/lib/__templates__/taro/src/lib/hooks/use-keyboard-offset.ts +37 -0
- package/lib/__templates__/taro/src/lib/measure.ts +115 -0
- package/lib/__templates__/taro/src/lib/platform.ts +12 -0
- package/lib/__templates__/taro/src/lib/utils.ts +6 -0
- package/lib/__templates__/taro/src/presets/dev-debug.ts +23 -0
- package/lib/__templates__/taro/src/presets/h5-container.tsx +15 -0
- package/lib/__templates__/taro/src/presets/h5-navbar.tsx +238 -0
- package/lib/__templates__/taro/src/presets/h5-styles.ts +220 -0
- package/lib/__templates__/taro/src/presets/index.tsx +18 -0
- package/lib/__templates__/templates.json +28 -22
- package/lib/__templates__/vite/AGENTS.md +41 -0
- package/lib/__templates__/vite/README.md +190 -11
- package/lib/__templates__/vite/_gitignore +1 -0
- package/lib/__templates__/vite/eslint.config.mjs +6 -1
- package/lib/__templates__/vite/package.json +14 -3
- package/lib/__templates__/vite/pnpm-lock.yaml +820 -1593
- package/lib/__templates__/vite/scripts/build.sh +4 -1
- package/lib/__templates__/vite/scripts/dev.sh +9 -2
- package/lib/__templates__/vite/scripts/start.sh +9 -3
- package/lib/__templates__/vite/server/routes/index.ts +31 -0
- package/lib/__templates__/vite/server/server.ts +65 -0
- package/lib/__templates__/vite/server/vite.ts +67 -0
- package/lib/__templates__/vite/src/main.ts +17 -47
- package/lib/__templates__/vite/template.config.js +49 -14
- package/lib/__templates__/vite/tsconfig.json +4 -3
- package/lib/__templates__/vite/vite.config.ts +5 -0
- package/lib/cli.js +160 -159
- package/package.json +7 -3
- package/lib/__templates__/taro/src/app.ts +0 -14
- package/lib/__templates__/taro/src/utils/h5-styles.ts +0 -22
- package/lib/__templates__/taro/src/utils/wx-debug.ts +0 -23
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
2
2
|
PREVIEW_DIR="${COZE_PREVIEW_DIR:-/source/preview}"
|
|
3
3
|
LOG_DIR="${COZE_LOG_DIR:-$ROOT_DIR/logs}"
|
|
4
|
-
|
|
4
|
+
LOG_CLIENT_FILE="$LOG_DIR/client.log"
|
|
5
5
|
mkdir -p "$LOG_DIR"
|
|
6
6
|
|
|
7
7
|
# ==================== 配置项 ====================
|
|
@@ -89,14 +89,17 @@ ensure_port() {
|
|
|
89
89
|
pipe_to_log() {
|
|
90
90
|
local source="${1:-CLIENT}"
|
|
91
91
|
local raw_log="${2:-}"
|
|
92
|
-
local line timestamp
|
|
92
|
+
local line clean_line timestamp
|
|
93
93
|
while IFS= read -r line || [ -n "$line" ]; do
|
|
94
|
+
clean_line=$(printf '%s' "$line" | sed 's/\x1b\[[0-9;]*[mA-Za-z]//g')
|
|
95
|
+
if echo "$clean_line" | grep -qE '(^(Web|iOS|Android) node_modules/.*expo-router.*entry\.js.*%|^(Web|iOS|Android) Bundled [0-9]+ms node_modules/.*expo-router.*entry\.js|Logs will appear in the browser)'; then
|
|
96
|
+
continue
|
|
97
|
+
fi
|
|
94
98
|
if [ -n "$raw_log" ]; then
|
|
95
|
-
|
|
99
|
+
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
100
|
+
echo "[$timestamp] $clean_line" >> "$raw_log"
|
|
96
101
|
fi
|
|
97
|
-
|
|
98
|
-
msg="${line}"
|
|
99
|
-
echo "$msg"
|
|
102
|
+
printf '[%s] %s\n' "$source" "$clean_line"
|
|
100
103
|
done
|
|
101
104
|
}
|
|
102
105
|
|
|
@@ -116,10 +119,10 @@ start_expo() {
|
|
|
116
119
|
|
|
117
120
|
if [ "$offline" = "1" ]; then
|
|
118
121
|
( EXPO_OFFLINE=1 EXPO_NO_DEPENDENCY_VALIDATION=1 EXPO_PUBLIC_BACKEND_BASE_URL="$EXPO_PUBLIC_BACKEND_BASE_URL" EXPO_PACKAGER_PROXY_URL="$EXPO_PACKAGER_PROXY_URL" EXPO_PUBLIC_COZE_PROJECT_ID="$EXPO_PUBLIC_COZE_PROJECT_ID" \
|
|
119
|
-
nohup npx expo start --clear --port "$EXPO_PORT" 2>&1 | pipe_to_log "CLIENT" "$
|
|
122
|
+
nohup npx expo start --clear --port "$EXPO_PORT" 2>&1 | pipe_to_log "CLIENT" "$LOG_CLIENT_FILE" ) &
|
|
120
123
|
else
|
|
121
124
|
( EXPO_NO_DEPENDENCY_VALIDATION=1 EXPO_PUBLIC_BACKEND_BASE_URL="$EXPO_PUBLIC_BACKEND_BASE_URL" EXPO_PACKAGER_PROXY_URL="$EXPO_PACKAGER_PROXY_URL" EXPO_PUBLIC_COZE_PROJECT_ID="$EXPO_PUBLIC_COZE_PROJECT_ID" \
|
|
122
|
-
nohup npx expo start --clear --port "$EXPO_PORT" 2>&1 | pipe_to_log "CLIENT" "$
|
|
125
|
+
nohup npx expo start --clear --port "$EXPO_PORT" 2>&1 | pipe_to_log "CLIENT" "$LOG_CLIENT_FILE" ) &
|
|
123
126
|
fi
|
|
124
127
|
EXPO_PID=$!
|
|
125
128
|
disown $EXPO_PID 2>/dev/null || true
|
|
@@ -130,9 +133,9 @@ start_expo() {
|
|
|
130
133
|
detect_expo_fetch_failed() {
|
|
131
134
|
local timeout="${1:-8}"
|
|
132
135
|
local waited=0
|
|
133
|
-
local log_file="$
|
|
136
|
+
local log_file="$LOG_CLIENT_FILE"
|
|
134
137
|
while [ "$waited" -lt "$timeout" ]; do
|
|
135
|
-
if [ -f "$log_file" ] &&
|
|
138
|
+
if [ -f "$log_file" ] && tail -n 100 "$log_file" 2>/dev/null | grep -q "TypeError: fetch failed"; then
|
|
136
139
|
return 0
|
|
137
140
|
fi
|
|
138
141
|
sleep 1
|
|
@@ -166,8 +169,6 @@ check_command "pnpm"
|
|
|
166
169
|
check_command "lsof"
|
|
167
170
|
check_command "bash"
|
|
168
171
|
|
|
169
|
-
echo "准备日志目录:$ROOT_DIR/logs"
|
|
170
|
-
mkdir -p "$ROOT_DIR/logs"
|
|
171
172
|
# 端口占用预检查与处理
|
|
172
173
|
ensure_port SERVER_PORT "$SERVER_PORT"
|
|
173
174
|
ensure_port EXPO_PORT "$EXPO_PORT"
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
4
4
|
SERVER_DIR="$ROOT_DIR/server"
|
|
5
|
-
LOG_DIR="$ROOT_DIR/logs"
|
|
6
|
-
|
|
5
|
+
LOG_DIR="${COZE_LOG_DIR:-$ROOT_DIR/logs}"
|
|
6
|
+
LOG_SERVER_FILE="$LOG_DIR/server.log"
|
|
7
7
|
SERVER_PORT="${SERVER_PORT:-9091}"
|
|
8
8
|
|
|
9
9
|
mkdir -p "$LOG_DIR"
|
|
@@ -11,13 +11,14 @@ mkdir -p "$LOG_DIR"
|
|
|
11
11
|
pipe_to_log() {
|
|
12
12
|
local source="${1:-SERVER}"
|
|
13
13
|
local raw_log="${2:-}"
|
|
14
|
-
local line
|
|
14
|
+
local line clean_line timestamp
|
|
15
15
|
while IFS= read -r line || [ -n "$line" ]; do
|
|
16
|
+
clean_line=$(printf '%s' "$line" | sed 's/\x1b\[[0-9;]*[mA-Za-z]//g')
|
|
16
17
|
if [ -n "$raw_log" ]; then
|
|
17
|
-
|
|
18
|
+
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
19
|
+
echo "[$timestamp] $clean_line" >> "$raw_log"
|
|
18
20
|
fi
|
|
19
|
-
|
|
20
|
-
echo "$line"
|
|
21
|
+
printf '[%s] %s\n' "$source" "$clean_line"
|
|
21
22
|
done
|
|
22
23
|
}
|
|
23
24
|
|
|
@@ -36,10 +37,10 @@ kill_old_server() {
|
|
|
36
37
|
echo "==================== Server Dev Run ===================="
|
|
37
38
|
echo "Server 目录:$SERVER_DIR"
|
|
38
39
|
echo "Server 端口:$SERVER_PORT"
|
|
39
|
-
echo "日志文件:$
|
|
40
|
+
echo "日志文件:$LOG_SERVER_FILE"
|
|
40
41
|
|
|
41
42
|
kill_old_server
|
|
42
43
|
|
|
43
44
|
echo "启动 server 服务..."
|
|
44
45
|
cd "$SERVER_DIR"
|
|
45
|
-
NODE_ENV=development PORT="$SERVER_PORT" npx tsx ./src/index.ts 2>&1 | pipe_to_log "SERVER" "$
|
|
46
|
+
NODE_ENV=development PORT="$SERVER_PORT" npx tsx watch ./src/index.ts 2>&1 | pipe_to_log "SERVER" "$LOG_SERVER_FILE"
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Expo App + Express.js
|
|
2
|
+
|
|
3
|
+
## 目录结构规范(严格遵循)
|
|
4
|
+
|
|
5
|
+
当前仓库是一个 monorepo(基于 pnpm 的 workspace)
|
|
6
|
+
|
|
7
|
+
- Expo 代码在 client 目录,Express.js 代码在 server 目录
|
|
8
|
+
- 本模板默认无 Tab Bar,可按需改造
|
|
9
|
+
|
|
10
|
+
目录结构说明
|
|
11
|
+
|
|
12
|
+
├── server/ # 服务端代码根目录 (Express.js)
|
|
13
|
+
| ├── src/
|
|
14
|
+
│ │ └── index.ts # Express 入口文件
|
|
15
|
+
| └── package.json # 服务端 package.json
|
|
16
|
+
├── client/ # React Native 前端代码
|
|
17
|
+
│ ├── app/ # Expo Router 路由目录(仅路由配置)
|
|
18
|
+
│ │ ├── _layout.tsx # 根布局文件(必需,务必阅读)
|
|
19
|
+
│ │ ├── home.tsx # 首页
|
|
20
|
+
│ │ └── index.tsx # re-export home.tsx
|
|
21
|
+
│ ├── screens/ # 页面实现目录(与 app/ 路由对应)
|
|
22
|
+
│ │ └── demo/ # demo 示例页面
|
|
23
|
+
│ │ ├── index.tsx # 页面组件实现
|
|
24
|
+
│ │ └── styles.ts # 页面样式
|
|
25
|
+
│ ├── components/ # 可复用组件
|
|
26
|
+
│ │ └── Screen.tsx # 页面容器组件(必用)
|
|
27
|
+
│ ├── hooks/ # 自定义 Hooks
|
|
28
|
+
│ ├── contexts/ # React Context 代码
|
|
29
|
+
│ ├── constants/ # 常量定义(如主题配置)
|
|
30
|
+
│ ├── utils/ # 工具函数
|
|
31
|
+
│ ├── assets/ # 静态资源
|
|
32
|
+
| └── package.json # Expo 应用 package.json
|
|
33
|
+
├── package.json
|
|
34
|
+
├── .cozeproj # 预置脚手架脚本(禁止修改)
|
|
35
|
+
└── .coze # 配置文件(禁止修改)
|
|
36
|
+
|
|
37
|
+
## 安装依赖
|
|
38
|
+
|
|
39
|
+
### 命令
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pnpm i
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 新增依赖约束
|
|
46
|
+
|
|
47
|
+
如果需要新增依赖,需在 client 和 server 各自的目录添加(原因:隔离前后端的依赖),禁止在根目录直接安装依赖
|
|
48
|
+
|
|
49
|
+
### 新增依赖标准流程
|
|
50
|
+
|
|
51
|
+
- 编辑 `client/package.json` 或 `server/package.json`
|
|
52
|
+
- 在根目录执行 `pnpm i`
|
|
53
|
+
|
|
54
|
+
## Expo 开发规范
|
|
55
|
+
|
|
56
|
+
### 路径别名
|
|
57
|
+
|
|
58
|
+
Expo 配置了 `@/` 路径别名指向 `client/` 目录:
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
// 正确
|
|
62
|
+
import { Screen } from '@/components/Screen';
|
|
63
|
+
|
|
64
|
+
// 避免相对路径
|
|
65
|
+
import { Screen } from '../../../components/Screen';
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## 本地开发
|
|
69
|
+
|
|
70
|
+
运行 coze dev 可以同时启动前端和后端服务,如果端口已占用,该命令会先杀掉占用端口的进程再启动,也可以用来重启前端和后端服务
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
coze dev
|
|
74
|
+
```
|
|
@@ -1,74 +1 @@
|
|
|
1
1
|
# Expo App + Express.js
|
|
2
|
-
|
|
3
|
-
## 目录结构规范(严格遵循)
|
|
4
|
-
|
|
5
|
-
当前仓库是一个 monorepo(基于 pnpm 的 workspace)
|
|
6
|
-
|
|
7
|
-
- Expo 代码在 client 目录,Express.js 代码在 server 目录
|
|
8
|
-
- 本模板默认无 Tab Bar,可按需改造
|
|
9
|
-
|
|
10
|
-
目录结构说明
|
|
11
|
-
|
|
12
|
-
├── server/ # 服务端代码根目录 (Express.js)
|
|
13
|
-
| ├── src/
|
|
14
|
-
│ │ └── index.ts # Express 入口文件
|
|
15
|
-
| └── package.json # 服务端 package.json
|
|
16
|
-
├── client/ # React Native 前端代码
|
|
17
|
-
│ ├── app/ # Expo Router 路由目录(仅路由配置)
|
|
18
|
-
│ │ ├── _layout.tsx # 根布局文件(必需,务必阅读)
|
|
19
|
-
│ │ ├── home.tsx # 首页
|
|
20
|
-
│ │ └── index.tsx # re-export home.tsx
|
|
21
|
-
│ ├── screens/ # 页面实现目录(与 app/ 路由对应)
|
|
22
|
-
│ │ └── demo/ # demo 示例页面
|
|
23
|
-
│ │ ├── index.tsx # 页面组件实现
|
|
24
|
-
│ │ └── styles.ts # 页面样式
|
|
25
|
-
│ ├── components/ # 可复用组件
|
|
26
|
-
│ │ └── Screen.tsx # 页面容器组件(必用)
|
|
27
|
-
│ ├── hooks/ # 自定义 Hooks
|
|
28
|
-
│ ├── contexts/ # React Context 代码
|
|
29
|
-
│ ├── constants/ # 常量定义(如主题配置)
|
|
30
|
-
│ ├── utils/ # 工具函数
|
|
31
|
-
│ ├── assets/ # 静态资源
|
|
32
|
-
| └── package.json # Expo 应用 package.json
|
|
33
|
-
├── package.json
|
|
34
|
-
├── .cozeproj # 预置脚手架脚本(禁止修改)
|
|
35
|
-
└── .coze # 配置文件(禁止修改)
|
|
36
|
-
|
|
37
|
-
## 安装依赖
|
|
38
|
-
|
|
39
|
-
### 命令
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
pnpm i
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
### 新增依赖约束
|
|
46
|
-
|
|
47
|
-
如果需要新增依赖,需在 client 和 server 各自的目录添加(原因:隔离前后端的依赖),禁止在根目录直接安装依赖
|
|
48
|
-
|
|
49
|
-
### 新增依赖标准流程
|
|
50
|
-
|
|
51
|
-
- 编辑 `client/package.json` 或 `server/package.json`
|
|
52
|
-
- 在根目录执行 `pnpm i`
|
|
53
|
-
|
|
54
|
-
## Expo 开发规范
|
|
55
|
-
|
|
56
|
-
### 路径别名
|
|
57
|
-
|
|
58
|
-
Expo 配置了 `@/` 路径别名指向 `client/` 目录:
|
|
59
|
-
|
|
60
|
-
```tsx
|
|
61
|
-
// 正确
|
|
62
|
-
import { Screen } from '@/components/Screen';
|
|
63
|
-
|
|
64
|
-
// 避免相对路径
|
|
65
|
-
import { Screen } from '../../../components/Screen';
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
## 本地开发
|
|
69
|
-
|
|
70
|
-
运行 coze dev 可以同时启动前端和后端服务,如果端口已占用,该命令会先杀掉占用端口的进程再启动,也可以用来重启前端和后端服务
|
|
71
|
-
|
|
72
|
-
```bash
|
|
73
|
-
coze dev
|
|
74
|
-
```
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
*
|
|
33
33
|
* ## 2. 沉浸式 Header (推荐)
|
|
34
34
|
* - 场景:Header 背景色/图片需要延伸到状态栏 (如首页、个人中心)。
|
|
35
|
-
* - 用法:`<Screen safeAreaEdges={['left', 'right', 'bottom']}>` (
|
|
35
|
+
* - 用法:`<Screen safeAreaEdges={['left', 'right', 'bottom']}>` (去掉 'top')
|
|
36
36
|
* - 配合:页面内部 Header 组件必须手动添加 paddingTop:
|
|
37
37
|
* ```tsx
|
|
38
38
|
* const insets = useSafeAreaInsets();
|
|
@@ -41,7 +41,7 @@ import {
|
|
|
41
41
|
*
|
|
42
42
|
* ## 3. 底部有 TabBar 或 悬浮按钮
|
|
43
43
|
* - 场景:页面底部有固定导航栏,或者需要精细控制底部留白。
|
|
44
|
-
* - 用法:`<Screen safeAreaEdges={['top', 'left', 'right']}>` (
|
|
44
|
+
* - 用法:`<Screen safeAreaEdges={['top', 'left', 'right']}>` (去掉 'bottom')
|
|
45
45
|
* - 配合:
|
|
46
46
|
* - 若是滚动页:`<ScrollView contentContainerStyle={{ paddingBottom: insets.bottom + 80 }}>`
|
|
47
47
|
* - 若是固定页:`<View style={{ paddingBottom: insets.bottom + 60 }}>`
|
|
@@ -8,6 +8,8 @@ import pluginImport from 'eslint-plugin-import';
|
|
|
8
8
|
import fontawesome6 from '../eslint-plugins/fontawesome6/index.js';
|
|
9
9
|
import reanimated from '../eslint-plugins/reanimated/index.js';
|
|
10
10
|
import reactnative from '../eslint-plugins/react-native/index.js';
|
|
11
|
+
import forbidEmoji from '../eslint-plugins/forbid-emoji/index.js';
|
|
12
|
+
import restrictLinearGradient from '../eslint-plugins/restrict-linear-gradient/index.js';
|
|
11
13
|
|
|
12
14
|
export default [
|
|
13
15
|
{
|
|
@@ -20,6 +22,7 @@ export default [
|
|
|
20
22
|
'tailwind.config.js', // 排除 Tailwind 配置文件
|
|
21
23
|
'**/*.d.ts',
|
|
22
24
|
'eslint.config.*',
|
|
25
|
+
'metro.config.*',
|
|
23
26
|
],
|
|
24
27
|
},
|
|
25
28
|
regexp.configs["flat/recommended"],
|
|
@@ -60,6 +63,8 @@ export default [
|
|
|
60
63
|
fontawesome6,
|
|
61
64
|
reanimated,
|
|
62
65
|
reactnative,
|
|
66
|
+
forbidEmoji,
|
|
67
|
+
restrictLinearGradient,
|
|
63
68
|
},
|
|
64
69
|
rules: {
|
|
65
70
|
// 关闭代码风格规则
|
|
@@ -80,6 +85,8 @@ export default [
|
|
|
80
85
|
'react/jsx-uses-react': 'off',
|
|
81
86
|
'fontawesome6/valid-name': 'error',
|
|
82
87
|
'reanimated/ban-mix-use': 'error',
|
|
88
|
+
'forbidEmoji/no-emoji': 'error',
|
|
89
|
+
'restrictLinearGradient/no-linear-gradient-backgroundcolor': 'error',
|
|
83
90
|
// 禁止使用 via.placeholder.com 服务
|
|
84
91
|
'no-restricted-syntax': [
|
|
85
92
|
'error',
|
|
@@ -15,38 +15,38 @@
|
|
|
15
15
|
"preset": "jest-expo"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@expo/metro-runtime": "
|
|
19
|
-
"@expo/vector-icons": "^15.0.
|
|
20
|
-
"@react-native-async-storage/async-storage": "
|
|
21
|
-
"@react-native-community/datetimepicker": "
|
|
22
|
-
"@react-native-community/slider": "
|
|
23
|
-
"@react-native-masked-view/masked-view": "
|
|
24
|
-
"@react-native-picker/picker": "
|
|
18
|
+
"@expo/metro-runtime": "~6.1.2",
|
|
19
|
+
"@expo/vector-icons": "^15.0.3",
|
|
20
|
+
"@react-native-async-storage/async-storage": "2.2.0",
|
|
21
|
+
"@react-native-community/datetimepicker": "8.4.4",
|
|
22
|
+
"@react-native-community/slider": "5.0.1",
|
|
23
|
+
"@react-native-masked-view/masked-view": "0.3.2",
|
|
24
|
+
"@react-native-picker/picker": "2.11.1",
|
|
25
25
|
"@react-navigation/bottom-tabs": "^7.2.0",
|
|
26
26
|
"@react-navigation/native": "^7.0.14",
|
|
27
27
|
"dayjs": "^1.11.19",
|
|
28
|
-
"expo": "54.0.
|
|
29
|
-
"expo-auth-session": "
|
|
30
|
-
"expo-av": "~16.0.
|
|
31
|
-
"expo-blur": "~15.0.
|
|
28
|
+
"expo": "54.0.33",
|
|
29
|
+
"expo-auth-session": "~7.0.10",
|
|
30
|
+
"expo-av": "~16.0.8",
|
|
31
|
+
"expo-blur": "~15.0.8",
|
|
32
32
|
"expo-camera": "~17.0.10",
|
|
33
|
-
"expo-constants": "~18.0.
|
|
34
|
-
"expo-crypto": "
|
|
33
|
+
"expo-constants": "~18.0.13",
|
|
34
|
+
"expo-crypto": "~15.0.8",
|
|
35
35
|
"expo-file-system": "~19.0.21",
|
|
36
|
-
"expo-font": "~14.0.
|
|
37
|
-
"expo-haptics": "~15.0.
|
|
38
|
-
"expo-image
|
|
39
|
-
"expo-
|
|
40
|
-
"expo-
|
|
41
|
-
"expo-
|
|
42
|
-
"expo-
|
|
43
|
-
"
|
|
44
|
-
"expo-
|
|
45
|
-
"expo-
|
|
46
|
-
"expo-
|
|
47
|
-
"expo-symbols": "~1.0.6",
|
|
36
|
+
"expo-font": "~14.0.11",
|
|
37
|
+
"expo-haptics": "~15.0.8",
|
|
38
|
+
"expo-image": "~3.0.11",
|
|
39
|
+
"expo-image-picker": "~17.0.10",
|
|
40
|
+
"expo-linear-gradient": "~15.0.8",
|
|
41
|
+
"expo-linking": "~8.0.11",
|
|
42
|
+
"expo-location": "~19.0.8",
|
|
43
|
+
"expo-router": "~6.0.23",
|
|
44
|
+
"expo-splash-screen": "~31.0.13",
|
|
45
|
+
"expo-status-bar": "~3.0.9",
|
|
46
|
+
"expo-symbols": "~1.0.8",
|
|
48
47
|
"expo-system-ui": "~6.0.9",
|
|
49
48
|
"expo-web-browser": "~15.0.10",
|
|
49
|
+
"js-base64": "^3.7.7",
|
|
50
50
|
"react": "19.1.0",
|
|
51
51
|
"react-dom": "19.1.0",
|
|
52
52
|
"react-native": "0.81.5",
|
|
@@ -54,25 +54,26 @@
|
|
|
54
54
|
"react-native-gesture-handler": "~2.28.0",
|
|
55
55
|
"react-native-keyboard-aware-scroll-view": "^0.9.5",
|
|
56
56
|
"react-native-modal-datetime-picker": "18.0.0",
|
|
57
|
-
"react-native-reanimated": "~4.1.
|
|
57
|
+
"react-native-reanimated": "~4.1.1",
|
|
58
58
|
"react-native-safe-area-context": "~5.6.0",
|
|
59
59
|
"react-native-screens": "~4.16.0",
|
|
60
|
-
"react-native-svg": "15.
|
|
60
|
+
"react-native-svg": "15.12.1",
|
|
61
61
|
"react-native-toast-message": "^2.3.3",
|
|
62
|
-
"react-native-web": "
|
|
63
|
-
"react-native-webview": "
|
|
62
|
+
"react-native-web": "~0.21.0",
|
|
63
|
+
"react-native-webview": "13.15.0",
|
|
64
64
|
"react-native-worklets": "0.5.1",
|
|
65
65
|
"zod": "^4.2.1"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"@babel/core": "^7.25.2",
|
|
69
|
-
"babel-plugin-module-resolver": "^5.0.2",
|
|
70
|
-
"babel-preset-expo": "^54.0.9",
|
|
71
69
|
"@eslint/js": "^9.27.0",
|
|
72
70
|
"@types/jest": "^29.5.12",
|
|
73
71
|
"@types/react": "~19.1.0",
|
|
74
72
|
"@types/react-test-renderer": "19.1.0",
|
|
73
|
+
"babel-plugin-module-resolver": "^5.0.2",
|
|
74
|
+
"babel-preset-expo": "^54.0.9",
|
|
75
75
|
"chalk": "^4.1.2",
|
|
76
|
+
"connect": "^3.7.0",
|
|
76
77
|
"depcheck": "^1.4.7",
|
|
77
78
|
"esbuild": "0.27.2",
|
|
78
79
|
"eslint": "^9.39.2",
|
|
@@ -83,13 +84,12 @@
|
|
|
83
84
|
"eslint-plugin-react-hooks": "^7.0.1",
|
|
84
85
|
"eslint-plugin-regexp": "^2.10.0",
|
|
85
86
|
"globals": "^16.1.0",
|
|
87
|
+
"http-proxy-middleware": "^3.0.5",
|
|
86
88
|
"jest": "^29.2.1",
|
|
87
|
-
"jest-expo": "~54.0.
|
|
89
|
+
"jest-expo": "~54.0.17",
|
|
88
90
|
"react-test-renderer": "19.1.0",
|
|
89
91
|
"tsx": "^4.21.0",
|
|
90
92
|
"typescript": "^5.8.3",
|
|
91
|
-
"typescript-eslint": "^8.32.1"
|
|
92
|
-
"connect": "^3.7.0",
|
|
93
|
-
"http-proxy-middleware": "^3.0.5"
|
|
93
|
+
"typescript-eslint": "^8.32.1"
|
|
94
94
|
}
|
|
95
95
|
}
|
|
@@ -15,10 +15,10 @@ export default function DemoPage() {
|
|
|
15
15
|
>
|
|
16
16
|
<Image
|
|
17
17
|
style={styles.logo}
|
|
18
|
-
source="https://lf-coze-web-cdn.coze.cn/obj/eden-cn/lm-lgvj/ljhwZthlaukjlkulzlp/coze-coding/
|
|
18
|
+
source="https://lf-coze-web-cdn.coze.cn/obj/eden-cn/lm-lgvj/ljhwZthlaukjlkulzlp/coze-coding/icon/coze-coding.gif"
|
|
19
19
|
></Image>
|
|
20
|
-
<Text style={{...styles.title, color: theme.textPrimary}}
|
|
21
|
-
<Text style={{...styles.description, color: theme.textSecondary}}
|
|
20
|
+
<Text style={{...styles.title, color: theme.textPrimary}}>应用开发中</Text>
|
|
21
|
+
<Text style={{...styles.description, color: theme.textSecondary}}>请稍候,界面即将呈现</Text>
|
|
22
22
|
</View>
|
|
23
23
|
</Screen>
|
|
24
24
|
);
|
|
@@ -9,7 +9,7 @@ const { execSync } = require('child_process');
|
|
|
9
9
|
const fs = require('fs');
|
|
10
10
|
const path = require('path');
|
|
11
11
|
|
|
12
|
-
console.log('
|
|
12
|
+
console.log('检测缺失的依赖...\n');
|
|
13
13
|
|
|
14
14
|
try {
|
|
15
15
|
// 运行 depcheck 并获取 JSON 输出
|
|
@@ -61,11 +61,11 @@ try {
|
|
|
61
61
|
});
|
|
62
62
|
|
|
63
63
|
if (missingPackages.length === 0) {
|
|
64
|
-
console.log('
|
|
64
|
+
console.log('没有发现缺失的依赖');
|
|
65
65
|
process.exit(0);
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
console.log('
|
|
68
|
+
console.log('发现以下缺失的依赖:');
|
|
69
69
|
missingPackages.forEach((pkg, index) => {
|
|
70
70
|
const files = missing[pkg];
|
|
71
71
|
console.log(` ${index + 1}. ${pkg}`);
|
|
@@ -74,7 +74,7 @@ try {
|
|
|
74
74
|
);
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
-
console.log('\n
|
|
77
|
+
console.log('\n开始安装...\n');
|
|
78
78
|
|
|
79
79
|
// 使用 expo install 安装所有缺失的包
|
|
80
80
|
const packagesToInstall = missingPackages.join(' ');
|
|
@@ -84,22 +84,22 @@ try {
|
|
|
84
84
|
stdio: 'inherit',
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
-
console.log('\n
|
|
87
|
+
console.log('\n所有缺失的依赖已安装完成');
|
|
88
88
|
} catch (installError) {
|
|
89
|
-
console.log('\
|
|
89
|
+
console.log('\nexpo install 失败,尝试使用 npm install...\n');
|
|
90
90
|
|
|
91
91
|
execSync(`npm install ${packagesToInstall}`, {
|
|
92
92
|
stdio: 'inherit',
|
|
93
93
|
});
|
|
94
94
|
|
|
95
|
-
console.log('\n
|
|
95
|
+
console.log('\n所有缺失的依赖已通过 npm 安装完成');
|
|
96
96
|
}
|
|
97
97
|
} catch (error) {
|
|
98
98
|
if (error.message.includes('depcheck')) {
|
|
99
|
-
console.error('
|
|
100
|
-
console.log('
|
|
99
|
+
console.error('depcheck 未安装或运行失败');
|
|
100
|
+
console.log('尝试运行: npm install -g depcheck');
|
|
101
101
|
} else {
|
|
102
|
-
console.error('
|
|
102
|
+
console.error('发生错误:', error.message);
|
|
103
103
|
}
|
|
104
104
|
process.exit(1);
|
|
105
105
|
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
const DEFAULTS = {
|
|
2
|
+
ignorePatterns: [],
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
const RE_EMOJI =
|
|
6
|
+
// 匹配完整 emoji 序列:基础图形 + 可选变体选择符/肤色 + 可选 ZWJ 连接序列;以及国旗与 keycap
|
|
7
|
+
/(?:\p{Extended_Pictographic}(?:\uFE0F|[\u{1F3FB}-\u{1F3FF}])?(?:\u200D\p{Extended_Pictographic}(?:\uFE0F|[\u{1F3FB}-\u{1F3FF}])?)*)|(?:[\u{1F1E6}-\u{1F1FF}]{2})|(?:[#*0-9]\uFE0F?\u20E3)/gu
|
|
8
|
+
|
|
9
|
+
function buildIgnoreRegexes(patterns) {
|
|
10
|
+
if (!Array.isArray(patterns)) return []
|
|
11
|
+
const result = []
|
|
12
|
+
for (const pattern of patterns) {
|
|
13
|
+
if (typeof pattern !== 'string') continue
|
|
14
|
+
try {
|
|
15
|
+
result.push(new RegExp(pattern))
|
|
16
|
+
} catch (_) {
|
|
17
|
+
continue
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return result
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function shouldIgnore(text, ignoreRegexes) {
|
|
24
|
+
if (!ignoreRegexes.length) return false
|
|
25
|
+
for (const re of ignoreRegexes) {
|
|
26
|
+
if (re.test(text)) return true
|
|
27
|
+
}
|
|
28
|
+
return false
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function hasEmoji(text) {
|
|
32
|
+
if (!text) return false
|
|
33
|
+
RE_EMOJI.lastIndex = 0
|
|
34
|
+
return RE_EMOJI.test(text)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function collectEmojis(text) {
|
|
38
|
+
if (!text) return []
|
|
39
|
+
RE_EMOJI.lastIndex = 0
|
|
40
|
+
const result = []
|
|
41
|
+
const seen = new Set()
|
|
42
|
+
for (const match of text.matchAll(RE_EMOJI)) {
|
|
43
|
+
if (match[0] && !seen.has(match[0])) {
|
|
44
|
+
seen.add(match[0])
|
|
45
|
+
result.push(match[0])
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return result
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = {
|
|
52
|
+
meta: {
|
|
53
|
+
type: 'problem',
|
|
54
|
+
docs: {
|
|
55
|
+
description: 'Disallow emoji in code',
|
|
56
|
+
recommended: 'error',
|
|
57
|
+
},
|
|
58
|
+
schema: [
|
|
59
|
+
{
|
|
60
|
+
type: 'object',
|
|
61
|
+
additionalProperties: false,
|
|
62
|
+
properties: {
|
|
63
|
+
ignorePatterns: {
|
|
64
|
+
type: 'array',
|
|
65
|
+
items: { type: 'string' },
|
|
66
|
+
default: DEFAULTS.ignorePatterns,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
messages: {
|
|
72
|
+
noEmoji: '禁止在代码中使用 emoji:{{emojis}},请移除或者使用 @expo/vector-icons 图标',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
create(context) {
|
|
77
|
+
const options = context.options && context.options[0] ? context.options[0] : {}
|
|
78
|
+
const ignoreRegexes = buildIgnoreRegexes(
|
|
79
|
+
Array.isArray(options.ignorePatterns) ? options.ignorePatterns : DEFAULTS.ignorePatterns
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
function checkText(node, text) {
|
|
83
|
+
if (!text) return
|
|
84
|
+
if (shouldIgnore(text, ignoreRegexes)) return
|
|
85
|
+
if (hasEmoji(text)) {
|
|
86
|
+
const emojis = collectEmojis(text)
|
|
87
|
+
const emojiText = emojis.length ? emojis.join(' ') : ''
|
|
88
|
+
context.report({ node, messageId: 'noEmoji', data: { emojis: emojiText } })
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
Literal(node) {
|
|
94
|
+
if (typeof node.value !== 'string') return
|
|
95
|
+
checkText(node, node.value)
|
|
96
|
+
},
|
|
97
|
+
TemplateLiteral(node) {
|
|
98
|
+
for (const quasi of node.quasis) {
|
|
99
|
+
const cooked = quasi.value && typeof quasi.value.cooked === 'string'
|
|
100
|
+
? quasi.value.cooked
|
|
101
|
+
: quasi.value && typeof quasi.value.raw === 'string'
|
|
102
|
+
? quasi.value.raw
|
|
103
|
+
: ''
|
|
104
|
+
checkText(quasi, cooked)
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
JSXText(node) {
|
|
108
|
+
checkText(node, node.value)
|
|
109
|
+
},
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
}
|