@coze-arch/cli 0.0.1-alpha.ee5d83 → 0.0.1-alpha.ef0249

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.
Files changed (33) hide show
  1. package/lib/__templates__/expo/.cozeproj/scripts/dev_run.sh +10 -10
  2. package/lib/__templates__/expo/.cozeproj/scripts/prod_build.sh +2 -2
  3. package/lib/__templates__/expo/.cozeproj/scripts/prod_run.sh +2 -2
  4. package/lib/__templates__/expo/.cozeproj/scripts/server_dev_run.sh +45 -0
  5. package/lib/__templates__/expo/README.md +2 -0
  6. package/lib/__templates__/expo/client/app/_layout.tsx +14 -14
  7. package/lib/__templates__/expo/client/app/index.tsx +1 -1
  8. package/lib/__templates__/expo/client/app.config.ts +4 -3
  9. package/lib/__templates__/expo/client/declarations.d.ts +5 -0
  10. package/lib/__templates__/expo/client/eslint.config.mjs +13 -10
  11. package/lib/__templates__/expo/client/hooks/useTheme.ts +26 -6
  12. package/lib/__templates__/expo/client/screens/{home → demo}/index.tsx +1 -1
  13. package/lib/__templates__/expo/client/scripts/install-missing-deps.js +1 -0
  14. package/lib/__templates__/expo/client/utils/index.ts +22 -0
  15. package/lib/__templates__/expo/eslint-plugins/fontawesome6/index.js +9 -0
  16. package/lib/__templates__/expo/eslint-plugins/fontawesome6/names.js +2486 -0
  17. package/lib/__templates__/expo/eslint-plugins/fontawesome6/rule.js +155 -0
  18. package/lib/__templates__/expo/server/build.js +21 -0
  19. package/lib/__templates__/expo/server/package.json +2 -2
  20. package/lib/__templates__/expo/server/src/index.ts +2 -1
  21. package/lib/__templates__/nextjs/next.config.ts +2 -2
  22. package/lib/__templates__/nextjs/package.json +6 -1
  23. package/lib/__templates__/nextjs/pnpm-lock.yaml +1461 -1710
  24. package/lib/__templates__/nextjs/src/app/page.tsx +2 -2
  25. package/lib/__templates__/nextjs/template.config.js +5 -5
  26. package/lib/__templates__/vite/package.json +6 -1
  27. package/lib/__templates__/vite/pnpm-lock.yaml +504 -982
  28. package/lib/__templates__/vite/src/main.ts +1 -2
  29. package/lib/__templates__/vite/template.config.js +6 -4
  30. package/lib/cli.js +13 -6
  31. package/package.json +1 -1
  32. package/lib/__templates__/expo/client/app/home.tsx +0 -1
  33. /package/lib/__templates__/expo/client/screens/{home → demo}/styles.ts +0 -0
@@ -1,4 +1,4 @@
1
- ROOT_DIR="$(cd "$(dirname "$0")" && pwd)"
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_FILE="$LOG_DIR/app.log"
@@ -111,14 +111,14 @@ wait_port_connectable() {
111
111
  start_expo() {
112
112
  local offline="${1:-0}"
113
113
 
114
- pushd ./client
114
+ pushd "$ROOT_DIR/client"
115
115
 
116
116
  if [ "$offline" = "1" ]; then
117
117
  ( EXPO_OFFLINE=1 EXPO_NO_DOCTOR=1 EXPO_PUBLIC_BACKEND_BASE_URL="$EXPO_PUBLIC_BACKEND_BASE_URL" EXPO_PACKAGER_PROXY_URL="$EXPO_PACKAGER_PROXY_URL" \
118
- nohup npx expo start --clear --port "$EXPO_PORT" 2>&1 | pipe_to_log "CLIENT" "../logs/client.log" ) &
118
+ nohup npx expo start --clear --port "$EXPO_PORT" 2>&1 | pipe_to_log "CLIENT" "$ROOT_DIR/logs/client.log" ) &
119
119
  else
120
120
  ( EXPO_NO_DOCTOR=1 EXPO_PUBLIC_BACKEND_BASE_URL="$EXPO_PUBLIC_BACKEND_BASE_URL" EXPO_PACKAGER_PROXY_URL="$EXPO_PACKAGER_PROXY_URL" \
121
- nohup npx expo start --clear --port "$EXPO_PORT" 2>&1 | pipe_to_log "CLIENT" "../logs/client.log" ) &
121
+ nohup npx expo start --clear --port "$EXPO_PORT" 2>&1 | pipe_to_log "CLIENT" "$ROOT_DIR/logs/client.log" ) &
122
122
  fi
123
123
  EXPO_PID=$!
124
124
  disown $EXPO_PID 2>/dev/null || true
@@ -129,7 +129,7 @@ start_expo() {
129
129
  detect_expo_fetch_failed() {
130
130
  local timeout="${1:-8}"
131
131
  local waited=0
132
- local log_file="logs/client.log"
132
+ local log_file="$ROOT_DIR/logs/client.log"
133
133
  while [ "$waited" -lt "$timeout" ]; do
134
134
  if [ -f "$log_file" ] && grep -q "TypeError: fetch failed" "$log_file" 2>/dev/null; then
135
135
  return 0
@@ -159,15 +159,15 @@ check_command "pnpm"
159
159
  check_command "lsof"
160
160
  check_command "bash"
161
161
 
162
- echo "准备日志目录:logs"
163
- mkdir -p logs
162
+ echo "准备日志目录:$ROOT_DIR/logs"
163
+ mkdir -p "$ROOT_DIR/logs"
164
164
  # 端口占用预检查与处理
165
165
  ensure_port SERVER_PORT "$SERVER_PORT"
166
166
  ensure_port EXPO_PORT "$EXPO_PORT"
167
167
 
168
168
  echo "==================== 启动 server 服务 ===================="
169
- echo "正在执行:npm run dev"
170
- ( PORT="$SERVER_PORT" nohup npm run dev --prefix ./server 2>&1 | pipe_to_log "SERVER" "logs/server.log" ) &
169
+ echo "正在执行:pnpm run dev (server)"
170
+ ( pushd "$ROOT_DIR/server" > /dev/null && SERVER_PORT="$SERVER_PORT" nohup pnpm run dev; popd > /dev/null ) &
171
171
  SERVER_PID=$!
172
172
  disown $SERVER_PID 2>/dev/null || true
173
173
  if [ -z "${SERVER_PID}" ]; then
@@ -181,7 +181,7 @@ start_expo 0
181
181
  if detect_expo_fetch_failed 8; then
182
182
  echo "Expo 启动检测到网络错误:TypeError: fetch failed,启用离线模式重试"
183
183
  if [ -n "${EXPO_PID}" ]; then kill -9 "$EXPO_PID" 2>/dev/null || true; fi
184
- : > logs/client.log
184
+ : > "$ROOT_DIR/logs/client.log"
185
185
  start_expo 1
186
186
  fi
187
187
  # 输出以下环境变量,确保 Expo 项目能正确连接到 Server 服务
@@ -40,8 +40,8 @@ fi
40
40
  info "==================== 依赖安装完成!====================\n"
41
41
 
42
42
  info "==================== dist打包 ===================="
43
- info "开始执行:npm run build --prefix server"
44
- (cd "$ROOT_DIR" && npm run build --prefix ./server) || error "dist打包失败"
43
+ info "开始执行:pnpm run build (server)"
44
+ (pushd "$ROOT_DIR/server" > /dev/null && pnpm run build; popd > /dev/null) || error "dist打包失败"
45
45
  info "==================== dist打包完成!====================\n"
46
46
 
47
47
  info "下一步:执行 ./prod_run.sh 启动服务"
@@ -29,6 +29,6 @@ check_command() {
29
29
  check_command "pnpm"
30
30
  check_command "npm"
31
31
 
32
- info "开始执行:npm run start"
33
- (cd "$ROOT_DIR" && PORT="$PORT" npm run start --prefix ./server) || error "服务启动失败"
32
+ info "开始执行:pnpm run start (server)"
33
+ (pushd "$ROOT_DIR/server" > /dev/null && PORT="$PORT" pnpm run start; popd > /dev/null) || error "服务启动失败"
34
34
  info "服务启动完成!\n"
@@ -0,0 +1,45 @@
1
+ #!/bin/bash
2
+
3
+ ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
4
+ SERVER_DIR="$ROOT_DIR/server"
5
+ LOG_DIR="$ROOT_DIR/logs"
6
+ LOG_FILE="$LOG_DIR/server.log"
7
+ SERVER_PORT="${SERVER_PORT:-9091}"
8
+
9
+ mkdir -p "$LOG_DIR"
10
+
11
+ pipe_to_log() {
12
+ local source="${1:-SERVER}"
13
+ local raw_log="${2:-}"
14
+ local line
15
+ while IFS= read -r line || [ -n "$line" ]; do
16
+ if [ -n "$raw_log" ]; then
17
+ echo "$line" >> "$raw_log"
18
+ fi
19
+ line=$(echo "[$source] $line" | sed 's/\x1b\[[0-9;]*[a-zA-Z]//g; s/\x1b\[[0-9;]*m//g')
20
+ echo "$line"
21
+ done
22
+ }
23
+
24
+ kill_old_server() {
25
+ if command -v lsof &> /dev/null; then
26
+ local pids
27
+ pids=$(lsof -t -i tcp:"$SERVER_PORT" -sTCP:LISTEN 2>/dev/null || true)
28
+ if [ -n "$pids" ]; then
29
+ echo "正在关闭旧的 server 进程:$pids"
30
+ kill -9 $pids 2>/dev/null || echo "关闭进程失败:$pids"
31
+ sleep 1
32
+ fi
33
+ fi
34
+ }
35
+
36
+ echo "==================== Server Dev Run ===================="
37
+ echo "Server 目录:$SERVER_DIR"
38
+ echo "Server 端口:$SERVER_PORT"
39
+ echo "日志文件:$LOG_FILE"
40
+
41
+ kill_old_server
42
+
43
+ echo "启动 server 服务..."
44
+ cd "$SERVER_DIR"
45
+ NODE_ENV=development PORT="$SERVER_PORT" npx tsx ./src/index.ts 2>&1 | pipe_to_log "SERVER" "$LOG_FILE"
@@ -26,6 +26,8 @@
26
26
  │ │ └── Screen.tsx # 页面容器组件(必用)
27
27
  │ ├── hooks/ # 自定义 Hooks
28
28
  │ ├── contexts/ # React Context 代码
29
+ │ ├── constants/ # 常量定义(如主题配置)
30
+ │ ├── utils/ # 工具函数
29
31
  │ ├── assets/ # 静态资源
30
32
  | └── package.json # Expo 应用 package.json
31
33
  ├── package.json
@@ -14,20 +14,20 @@ LogBox.ignoreLogs([
14
14
  export default function RootLayout() {
15
15
  return (
16
16
  <AuthProvider>
17
- <GestureHandlerRootView style={{ flex: 1 }}>
18
- <StatusBar style="dark"></StatusBar>
19
- <Stack screenOptions={{
20
- // 设置所有页面的切换动画为从右侧滑入,适用于iOS 和 Android
21
- animation: 'slide_from_right',
22
- gestureEnabled: true,
23
- gestureDirection: 'horizontal',
24
- // 隐藏自带的头部
25
- headerShown: false
26
- }}>
27
- <Stack.Screen name="index" options={{ title: "" }} />
28
- </Stack>
29
- <Toast />
30
- </GestureHandlerRootView>
17
+ <GestureHandlerRootView style={{ flex: 1 }}>
18
+ <StatusBar style="dark"></StatusBar>
19
+ <Stack screenOptions={{
20
+ // 设置所有页面的切换动画为从右侧滑入,适用于iOS 和 Android
21
+ animation: 'slide_from_right',
22
+ gestureEnabled: true,
23
+ gestureDirection: 'horizontal',
24
+ // 隐藏自带的头部
25
+ headerShown: false
26
+ }}>
27
+ <Stack.Screen name="index" options={{ title: "" }} />
28
+ </Stack>
29
+ <Toast />
30
+ </GestureHandlerRootView>
31
31
  </AuthProvider>
32
32
  );
33
33
  }
@@ -1 +1 @@
1
- export { default } from './home'
1
+ export { default } from '@/screens/demo';
@@ -1,7 +1,8 @@
1
1
  import { ExpoConfig, ConfigContext } from 'expo/config';
2
2
 
3
- const appName = 'My App';
4
- const slugAppName = 'my-app';
3
+ const appName = process.env.EXPO_PUBLIC_COZE_PROJECT_NAME || '应用';
4
+ const projectId = process.env.EXPO_PUBLIC_COZE_PROJECT_ID;
5
+ const slugAppName = projectId ? `app${projectId}` : 'myapp';
5
6
 
6
7
  export default ({ config }: ConfigContext): ExpoConfig => {
7
8
  return {
@@ -22,7 +23,7 @@ export default ({ config }: ConfigContext): ExpoConfig => {
22
23
  "foregroundImage": "./assets/images/adaptive-icon.png",
23
24
  "backgroundColor": "#ffffff"
24
25
  },
25
- "package": "com.anonymous.myapp"
26
+ "package": `com.anonymous.x${projectId || '0'}`
26
27
  },
27
28
  "web": {
28
29
  "bundler": "metro",
@@ -0,0 +1,5 @@
1
+ // declarations.d.ts
2
+
3
+ declare module 'expo-file-system/legacy' {
4
+ export * from 'expo-file-system';
5
+ }
@@ -1,10 +1,11 @@
1
- import js from "@eslint/js";
2
- import globals from "globals";
3
- import tseslint from "typescript-eslint";
4
- import pluginReact from "eslint-plugin-react";
5
- import reactHooks from "eslint-plugin-react-hooks";
6
- import regexp from "eslint-plugin-regexp";
7
- import pluginImport from "eslint-plugin-import";
1
+ import js from '@eslint/js';
2
+ import globals from 'globals';
3
+ import tseslint from 'typescript-eslint';
4
+ import pluginReact from 'eslint-plugin-react';
5
+ import reactHooks from 'eslint-plugin-react-hooks';
6
+ import regexp from 'eslint-plugin-regexp';
7
+ import pluginImport from 'eslint-plugin-import';
8
+ import fontawesome6 from '../eslint-plugins/fontawesome6/index.js';
8
9
 
9
10
  export default [
10
11
  {
@@ -20,14 +21,14 @@ export default [
20
21
  regexp.configs["flat/recommended"],
21
22
  js.configs.recommended,
22
23
  ...tseslint.configs.recommended,
23
-
24
+
24
25
  // React 的推荐配置
25
26
  pluginReact.configs.flat.recommended,
26
27
  pluginReact.configs.flat['jsx-runtime'],
27
28
  reactHooks.configs.flat.recommended,
28
29
  {
29
30
  files: ["**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
30
-
31
+
31
32
  // 语言选项:设置全局变量
32
33
  languageOptions: {
33
34
  globals: {
@@ -52,6 +53,7 @@ export default [
52
53
 
53
54
  plugins: {
54
55
  import: pluginImport,
56
+ fontawesome6,
55
57
  },
56
58
  rules: {
57
59
  // 关闭代码风格规则
@@ -70,6 +72,7 @@ export default [
70
72
  'no-prototype-builtins': 'off',
71
73
  'react/react-in-jsx-scope': 'off',
72
74
  'react/jsx-uses-react': 'off',
75
+ 'fontawesome6/valid-name': 'error',
73
76
  },
74
77
  },
75
78
 
@@ -91,7 +94,7 @@ export default [
91
94
  // 在 .js 文件中关闭 TS 规则
92
95
  '@typescript-eslint/no-require-imports': 'off',
93
96
  // 在 Node.js 文件中允许 require
94
- '@typescript-eslint/no-var-requires': 'off',
97
+ '@typescript-eslint/no-var-requires': 'off',
95
98
  'no-undef': 'off',
96
99
  },
97
100
  },
@@ -1,13 +1,33 @@
1
- import { Colors } from "@/constants/theme";
2
- import { useColorScheme } from "@/hooks/useColorScheme";
1
+ import { Colors } from '@/constants/theme';
2
+ import { useColorScheme } from '@/hooks/useColorScheme';
3
3
 
4
- export function useTheme() {
5
- const colorScheme = useColorScheme();
6
- const isDark = colorScheme === "dark";
7
- const theme = Colors[(colorScheme as "light" | "dark") ?? "light"];
4
+ enum COLOR_SCHEME_CHOICE {
5
+ FOLLOW_SYSTEM = 'follow-system', // 跟随系统自动变化
6
+ DARK = 'dark', // 固定为 dark 主题,不随系统变化
7
+ LIGHT = 'light', // 固定为 light 主题,不随系统变化
8
+ };
9
+
10
+ const userPreferColorScheme: COLOR_SCHEME_CHOICE = COLOR_SCHEME_CHOICE.FOLLOW_SYSTEM;
11
+
12
+ function getTheme(colorScheme?: 'dark' | 'light' | null) {
13
+ const isDark = colorScheme === 'dark';
14
+ const theme = Colors[colorScheme ?? 'light'];
8
15
 
9
16
  return {
10
17
  theme,
11
18
  isDark,
12
19
  };
13
20
  }
21
+
22
+ function useTheme() {
23
+ const systemColorScheme = useColorScheme()
24
+ const colorScheme = userPreferColorScheme === COLOR_SCHEME_CHOICE.FOLLOW_SYSTEM ?
25
+ systemColorScheme :
26
+ userPreferColorScheme;
27
+
28
+ return getTheme(colorScheme);
29
+ }
30
+
31
+ export {
32
+ useTheme,
33
+ }
@@ -5,7 +5,7 @@ import { useTheme } from '@/hooks/useTheme';
5
5
  import { Screen } from '@/components/Screen';
6
6
  import { styles } from './styles';
7
7
 
8
- export default function HomeScreen() {
8
+ export default function DemoPage() {
9
9
  const { theme, isDark } = useTheme();
10
10
 
11
11
  return (
@@ -38,6 +38,7 @@ try {
38
38
  const ignoreFilePatterns = [
39
39
  /template\.config\.(ts|js)$/, // 模板配置文件
40
40
  /\.template\./, // 其他模板文件
41
+ /declarations\.d\.ts$/, // 项目配置文件
41
42
  ];
42
43
 
43
44
  // 过滤包:排除内部别名和只被模板文件引用的包
@@ -1,9 +1,31 @@
1
+ import { Platform } from 'react-native';
1
2
  import dayjs from 'dayjs';
2
3
  import utc from 'dayjs/plugin/utc';
3
4
  dayjs.extend(utc);
4
5
 
5
6
  const API_BASE = (process.env.EXPO_PUBLIC_API_BASE ?? '').replace(/\/$/, '');
6
7
 
8
+ /**
9
+ * 创建跨平台兼容的文件对象,用于 FormData.append()
10
+ * - Web 端返回 File 对象
11
+ * - 移动端返回 { uri, type, name } 对象(RN fetch 会自动处理)
12
+ * @param fileUri Expo 媒体库(如 expo-image-picker、expo-camera)返回的 uri
13
+ * @param fileName 上传时的文件名,如 'photo.jpg'
14
+ * @param mimeType 文件 MIME 类型,如 'image/jpeg'、'audio/mpeg'
15
+ */
16
+ export async function createFormDataFile(
17
+ fileUri: string,
18
+ fileName: string,
19
+ mimeType: string
20
+ ): Promise<File | { uri: string; type: string; name: string }> {
21
+ if (Platform.OS === 'web') {
22
+ const response = await fetch(fileUri);
23
+ const blob = await response.blob();
24
+ return new File([blob], fileName, { type: mimeType });
25
+ }
26
+ return { uri: fileUri, type: mimeType, name: fileName };
27
+ }
28
+
7
29
  /**
8
30
  * 构建文件或图片完整的URL
9
31
  * @param url 相对或绝对路径
@@ -0,0 +1,9 @@
1
+ const validName = require('./rule')
2
+
3
+ const plugin = {
4
+ rules: {
5
+ 'valid-name': validName,
6
+ },
7
+ };
8
+
9
+ module.exports = plugin