@coze-arch/cli 0.1.5 → 0.1.8-alpha.6e99f9
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/docs/deploy.md +12 -1
- package/lib/__templates__/expo/.cozeproj/scripts/dev_build.sh +3 -3
- package/lib/__templates__/expo/.cozeproj/scripts/dev_run.sh +11 -3
- package/lib/__templates__/expo/.cozeproj/scripts/prepare-node-modules.sh +382 -0
- package/lib/__templates__/expo/.cozeproj/scripts/prod_build.sh +3 -3
- package/lib/__templates__/expo/client/components/Screen.tsx +65 -25
- package/lib/__templates__/expo/client/heroui/components/toast/toast.tsx +6 -2
- package/lib/__templates__/expo/client/heroui/helpers/internal/hooks/use-controllable-state.ts +1 -1
- package/lib/__templates__/nextjs/eslint.config.mjs +11 -5
- package/lib/__templates__/nextjs/next.config.ts +10 -0
- package/lib/__templates__/nextjs/package.json +2 -5
- package/lib/__templates__/nextjs/pnpm-lock.yaml +14 -945
- package/lib/__templates__/nextjs/scripts/build.ps1 +1 -1
- package/lib/__templates__/nextjs/scripts/build.sh +2 -2
- package/lib/__templates__/nextjs/scripts/dev.ps1 +2 -2
- package/lib/__templates__/nextjs/scripts/dev.sh +93 -10
- package/lib/__templates__/nextjs/scripts/prepare-next-dev-output.sh +122 -0
- package/lib/__templates__/nextjs/scripts/prepare-node-modules.sh +382 -0
- package/lib/__templates__/nextjs/scripts/prepare.sh +1 -4
- package/lib/__templates__/nextjs/src/app/layout.tsx +0 -4
- package/lib/__templates__/nextjs/src/server.ts +1 -1
- package/lib/__templates__/nuxt-vue/scripts/build.sh +1 -1
- package/lib/__templates__/nuxt-vue/scripts/dev.sh +11 -3
- package/lib/__templates__/nuxt-vue/scripts/prepare-node-modules.sh +382 -0
- package/lib/__templates__/nuxt-vue/scripts/prepare.sh +1 -4
- package/lib/__templates__/taro/.cozeproj/scripts/deploy_build.sh +5 -1
- package/lib/__templates__/taro/.cozeproj/scripts/deploy_run.sh +4 -0
- package/lib/__templates__/taro/.cozeproj/scripts/dev_build.sh +3 -0
- package/lib/__templates__/taro/.cozeproj/scripts/dev_run.ps1 +4 -2
- package/lib/__templates__/taro/.cozeproj/scripts/dev_run.sh +29 -7
- package/lib/__templates__/taro/.cozeproj/scripts/init_env.sh +4 -0
- package/lib/__templates__/taro/.cozeproj/scripts/pack.ps1 +3 -1
- package/lib/__templates__/taro/.cozeproj/scripts/pack.sh +8 -0
- package/lib/__templates__/taro/.cozeproj/scripts/prepare-node-modules.sh +382 -0
- package/lib/__templates__/taro/.cozeproj/scripts/validate.ps1 +3 -1
- package/lib/__templates__/taro/.cozeproj/scripts/validate.sh +4 -0
- package/lib/__templates__/taro/_gitignore +1 -0
- package/lib/__templates__/vite/scripts/build.sh +1 -1
- package/lib/__templates__/vite/scripts/dev.ps1 +2 -2
- package/lib/__templates__/vite/scripts/dev.sh +11 -3
- package/lib/__templates__/vite/scripts/prepare-node-modules.sh +382 -0
- package/lib/__templates__/vite/scripts/prepare.sh +1 -4
- package/lib/cli.js +147 -16
- package/package.json +2 -2
- package/lib/__templates__/nextjs/.babelrc +0 -15
package/docs/deploy.md
CHANGED
|
@@ -57,7 +57,7 @@ coze-dev deploy app rename <name> --app-id <app_id> --yes
|
|
|
57
57
|
coze-dev deploy app delete --app-id <app_id> --yes
|
|
58
58
|
coze-dev deploy app analytics|metrics --app-id <app_id> [--time-range <range>]
|
|
59
59
|
coze-dev deploy app usage --app-id <app_id> --bill-month <YYYY-MM>
|
|
60
|
-
coze-dev deploy [--lane <ppe_lane>] [--source-root <directory>] [--app-id <app_id>] [--application-type <type>] [--name <name>] [--connector-id <id>]
|
|
60
|
+
coze-dev deploy [--lane <ppe_lane>] [--source-root <directory>] [--app-id <app_id>] [--application-type <type>] [--name <name>] [--deploy-config <json>] [--connector-id <id>]
|
|
61
61
|
coze-dev deploy status|list|online|rollback-check|cancel|rollback|stop|resume|pause
|
|
62
62
|
coze-dev deploy logs|log-count|count|build-log|config
|
|
63
63
|
coze-dev deploy domain list [--unit-type dev|prod]
|
|
@@ -76,6 +76,17 @@ coze-dev deploy mini-program status|bind|submit-code|unbind|auth|preview
|
|
|
76
76
|
Application;类型不一致时创建新 Application、覆盖 `app_id` 并提示。也可用 `--project-id`
|
|
77
77
|
或 `--app-id` 显式指定已有资源,显式指定时不会额外创建 Project。
|
|
78
78
|
|
|
79
|
+
部署预检默认返回服务端给出的 `deploy_config`。需要调整部署规格时,传入包含完整四个字段的
|
|
80
|
+
`--deploy-config` JSON;预检会返回覆盖后的最终配置,确认部署时必须原样复用该参数:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
coze-dev deploy --source-root . \
|
|
84
|
+
--deploy-config '{"cpu":4,"memory_mb":8192,"max_instance":2,"max_concurrency":100}'
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
CPU 与内存必须使用固定组合:`1/2048`、`4/8192`、`8/16384`、`16/32768`;
|
|
88
|
+
`max_instance` 为 1–100,`max_concurrency` 为 10–1000。四个值均为整数。
|
|
89
|
+
|
|
79
90
|
首次部署的应用名称可通过 `--name` 指定;显式参数优先于 `.coze` 的
|
|
80
91
|
`[project].name`。Pages 部署没有 `.coze` 时,CLI 会先生成最小配置再写入绑定。
|
|
81
92
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
if [ -z "${BASH_VERSION:-}" ]; then exec /usr/bin/env bash "$0" "$@"; fi
|
|
3
3
|
set -euo pipefail
|
|
4
|
-
ROOT_DIR="$(pwd)"
|
|
4
|
+
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
5
5
|
PREVIEW_DIR="/source/preview"
|
|
6
6
|
SOURCE_DIR="/source"
|
|
7
7
|
|
|
@@ -34,8 +34,8 @@ echo "==================== 安装项目依赖 ===================="
|
|
|
34
34
|
if [ ! -f "package.json" ]; then
|
|
35
35
|
echo "项目目录下无 package.json,不是合法的 Node.js 项目"
|
|
36
36
|
fi
|
|
37
|
-
# 步骤 2.1/2.2
|
|
38
|
-
|
|
37
|
+
# 步骤 2.1/2.2:通过受管入口安装项目依赖
|
|
38
|
+
bash "$ROOT_DIR/.cozeproj/scripts/prepare-node-modules.sh" --registry=https://registry.npmmirror.com
|
|
39
39
|
|
|
40
40
|
echo "检查根目录 post_install.py"
|
|
41
41
|
if [ -f "$PREVIEW_DIR/post_install.py" ]; then
|
|
@@ -123,11 +123,15 @@ wait_port_connectable() {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
# detached 出去的进程没人负责回收,超过这个时长就自己退出,避免端口与内存长期泄露。
|
|
126
|
-
MAX_RUNTIME_SECONDS=
|
|
126
|
+
MAX_RUNTIME_SECONDS=3600
|
|
127
|
+
|
|
128
|
+
timeout_watchdog_enabled() {
|
|
129
|
+
[[ -z "${COZE_EVAL:-}" && -z "${COZE_PROJECT_TYPE:-}" ]]
|
|
130
|
+
}
|
|
127
131
|
|
|
128
132
|
# 真正被 detach 的是这层 bash wrapper:它是进程组 leader,组内 watchdog 到点回收整组
|
|
129
133
|
# (wrapper -> pnpm -> expo/node);被包的进程自己先退出时也顺手清空进程组,不留残余。
|
|
130
|
-
RUN_WITH_TIMEOUT='
|
|
134
|
+
RUN_WITH_TIMEOUT="$(declare -f timeout_watchdog_enabled)"'
|
|
131
135
|
timeout_seconds=$1
|
|
132
136
|
shift
|
|
133
137
|
|
|
@@ -135,6 +139,7 @@ shift
|
|
|
135
139
|
child_pid=$!
|
|
136
140
|
|
|
137
141
|
# 先忽略 TERM,才能在向整组发 TERM(自己也在组里)之后存活下来补一发 KILL。
|
|
142
|
+
if timeout_watchdog_enabled; then
|
|
138
143
|
( trap "" TERM
|
|
139
144
|
sleep "${timeout_seconds}"
|
|
140
145
|
echo "[dev] 后台进程运行超过 ${timeout_seconds}s,回收进程组 $$。"
|
|
@@ -142,6 +147,7 @@ child_pid=$!
|
|
|
142
147
|
sleep 5
|
|
143
148
|
kill -KILL -- "-$$" 2>/dev/null || true
|
|
144
149
|
) &
|
|
150
|
+
fi
|
|
145
151
|
|
|
146
152
|
wait "${child_pid}"
|
|
147
153
|
kill -KILL -- "-$$" 2>/dev/null || true
|
|
@@ -290,7 +296,9 @@ if [ -z "${EXPO_PID}" ]; then
|
|
|
290
296
|
fi
|
|
291
297
|
|
|
292
298
|
echo "所有服务已启动。Server PID: ${SERVER_PID}, Expo PID: ${EXPO_PID}"
|
|
293
|
-
|
|
299
|
+
if timeout_watchdog_enabled; then
|
|
300
|
+
echo "两个服务均在 ${MAX_RUNTIME_SECONDS}s 后自动退出。"
|
|
301
|
+
fi
|
|
294
302
|
|
|
295
303
|
echo "检查 Server 服务端口:$SERVER_HOST:$SERVER_PORT"
|
|
296
304
|
if wait_port_connectable "$SERVER_HOST" "$SERVER_PORT" 10 2; then
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Install dependencies on local disk and expose them to the source tree by symlink.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
|
6
|
+
if [ "$(basename "$(dirname "$SCRIPT_DIR")")" = ".cozeproj" ]; then
|
|
7
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd -P)"
|
|
8
|
+
else
|
|
9
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)"
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
command -v pnpm >/dev/null 2>&1 || {
|
|
13
|
+
echo "[node_modules] pnpm is required" >&2
|
|
14
|
+
exit 1
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
DRIVE_ROOT="${COZE_DRIVE_ROOT:-/Coze/Drive}"
|
|
18
|
+
if [ -d "$DRIVE_ROOT" ]; then
|
|
19
|
+
DRIVE_ROOT="$(cd "$DRIVE_ROOT" && pwd -P)"
|
|
20
|
+
else
|
|
21
|
+
DRIVE_ROOT="${DRIVE_ROOT%/}"
|
|
22
|
+
fi
|
|
23
|
+
case "$PROJECT_ROOT" in
|
|
24
|
+
"$DRIVE_ROOT"|"$DRIVE_ROOT"/*) ;;
|
|
25
|
+
*)
|
|
26
|
+
(cd "$PROJECT_ROOT" && pnpm install "$@")
|
|
27
|
+
exit 0
|
|
28
|
+
;;
|
|
29
|
+
esac
|
|
30
|
+
|
|
31
|
+
NM_ROOT="/tmp/nm"
|
|
32
|
+
PROJECT_ID="$(basename "$PROJECT_ROOT")-$(printf '%s' "$PROJECT_ROOT" | cksum | awk '{print $1}')"
|
|
33
|
+
mkdir -p "$NM_ROOT"
|
|
34
|
+
WORK_DIR="$NM_ROOT/$PROJECT_ID"
|
|
35
|
+
case "$WORK_DIR" in
|
|
36
|
+
"$DRIVE_ROOT"|"$DRIVE_ROOT"/*)
|
|
37
|
+
echo "[node_modules] local dependency directory must not be on Coze Drive: $WORK_DIR" >&2
|
|
38
|
+
exit 1
|
|
39
|
+
;;
|
|
40
|
+
esac
|
|
41
|
+
|
|
42
|
+
LOCK_DIR="$NM_ROOT/$PROJECT_ID.lock"
|
|
43
|
+
LOCK_TIMEOUT_SECONDS="${COZE_NODE_MODULES_LOCK_TIMEOUT_SECONDS:-120}"
|
|
44
|
+
case "$LOCK_TIMEOUT_SECONDS" in
|
|
45
|
+
''|*[!0-9]*|0)
|
|
46
|
+
echo "[node_modules] COZE_NODE_MODULES_LOCK_TIMEOUT_SECONDS must be a positive integer" >&2
|
|
47
|
+
exit 1
|
|
48
|
+
;;
|
|
49
|
+
esac
|
|
50
|
+
|
|
51
|
+
lock_owner_is_alive() {
|
|
52
|
+
local owner_pid=""
|
|
53
|
+
[ -f "$LOCK_DIR/pid" ] || return 1
|
|
54
|
+
IFS= read -r owner_pid < "$LOCK_DIR/pid" || return 1
|
|
55
|
+
case "$owner_pid" in
|
|
56
|
+
''|*[!0-9]*) return 1 ;;
|
|
57
|
+
esac
|
|
58
|
+
kill -0 "$owner_pid" 2>/dev/null
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
release_lock() {
|
|
62
|
+
local owner_pid=""
|
|
63
|
+
[ -f "$LOCK_DIR/pid" ] || return 0
|
|
64
|
+
IFS= read -r owner_pid < "$LOCK_DIR/pid" || true
|
|
65
|
+
if [ "$owner_pid" = "$$" ]; then
|
|
66
|
+
rm -f "$LOCK_DIR/pid"
|
|
67
|
+
rmdir "$LOCK_DIR" 2>/dev/null || true
|
|
68
|
+
fi
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
lock_started_at="$(date +%s)"
|
|
72
|
+
missing_owner_checks=0
|
|
73
|
+
while ! mkdir "$LOCK_DIR" 2>/dev/null; do
|
|
74
|
+
if lock_owner_is_alive; then
|
|
75
|
+
missing_owner_checks=0
|
|
76
|
+
else
|
|
77
|
+
missing_owner_checks=$((missing_owner_checks + 1))
|
|
78
|
+
if [ "$missing_owner_checks" -ge 2 ]; then
|
|
79
|
+
stale_lock_dir="$LOCK_DIR.stale.$$"
|
|
80
|
+
if mv "$LOCK_DIR" "$stale_lock_dir" 2>/dev/null; then
|
|
81
|
+
echo "[node_modules] Recovering stale dependency preparation lock."
|
|
82
|
+
rm -rf "$stale_lock_dir"
|
|
83
|
+
fi
|
|
84
|
+
missing_owner_checks=0
|
|
85
|
+
continue
|
|
86
|
+
fi
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
lock_now="$(date +%s)"
|
|
90
|
+
if [ $((lock_now - lock_started_at)) -ge "$LOCK_TIMEOUT_SECONDS" ]; then
|
|
91
|
+
echo "[node_modules] Timed out waiting ${LOCK_TIMEOUT_SECONDS}s for dependency preparation lock: $LOCK_DIR" >&2
|
|
92
|
+
exit 1
|
|
93
|
+
fi
|
|
94
|
+
echo "[node_modules] Waiting for another dependency preparation for this project..."
|
|
95
|
+
sleep 1
|
|
96
|
+
done
|
|
97
|
+
printf '%s\n' "$$" > "$LOCK_DIR/pid"
|
|
98
|
+
trap release_lock EXIT
|
|
99
|
+
|
|
100
|
+
mkdir -p "$WORK_DIR"
|
|
101
|
+
INSTALL_STATE_FILE="$WORK_DIR/.install-state"
|
|
102
|
+
|
|
103
|
+
# Previous versions mirrored the whole project into WORK_DIR. That gives pnpm
|
|
104
|
+
# and framework dev servers two paths for the same source tree, which breaks
|
|
105
|
+
# file watching and on-demand compilation. Keep only managed local outputs.
|
|
106
|
+
cleanup_legacy_workspace() {
|
|
107
|
+
local workspace_dir="$1" entry name
|
|
108
|
+
if [ -L "$workspace_dir" ] || { [ -e "$workspace_dir" ] && [ ! -d "$workspace_dir" ]; }; then
|
|
109
|
+
rm -rf "$workspace_dir"
|
|
110
|
+
return
|
|
111
|
+
fi
|
|
112
|
+
if [ ! -d "$workspace_dir" ]; then
|
|
113
|
+
return 0
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
while IFS= read -r -d '' entry; do
|
|
117
|
+
name="$(basename "$entry")"
|
|
118
|
+
[ "$name" = "node_modules" ] || rm -rf "$entry"
|
|
119
|
+
done < <(find "$workspace_dir" -mindepth 1 -maxdepth 1 -print0)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
# caches is reserved for the sibling prepare scripts of this project. They keep
|
|
123
|
+
# regenerable framework output there (the Next helper points .next/dev into it), and that
|
|
124
|
+
# output has to outlive an install, so this cleanup must not read it as leftover.
|
|
125
|
+
cleanup_legacy_shadow_project() {
|
|
126
|
+
local entry name
|
|
127
|
+
while IFS= read -r -d '' entry; do
|
|
128
|
+
name="$(basename "$entry")"
|
|
129
|
+
case "$name" in
|
|
130
|
+
node_modules|client|server|caches|.install-state) continue ;;
|
|
131
|
+
esac
|
|
132
|
+
rm -rf "$entry"
|
|
133
|
+
done < <(find "$WORK_DIR" -mindepth 1 -maxdepth 1 -print0)
|
|
134
|
+
|
|
135
|
+
for workspace in client server; do
|
|
136
|
+
cleanup_legacy_workspace "$WORK_DIR/$workspace"
|
|
137
|
+
done
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
link_install_entry() {
|
|
141
|
+
local source_path="$1" target_path="$2"
|
|
142
|
+
rm -rf "$target_path"
|
|
143
|
+
if [ -e "$source_path" ]; then
|
|
144
|
+
ln -s "$source_path" "$target_path"
|
|
145
|
+
fi
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
hash_install_input() {
|
|
149
|
+
if command -v sha256sum >/dev/null 2>&1; then
|
|
150
|
+
sha256sum | awk '{print $1}'
|
|
151
|
+
elif command -v shasum >/dev/null 2>&1; then
|
|
152
|
+
shasum -a 256 | awk '{print $1}'
|
|
153
|
+
else
|
|
154
|
+
cksum | awk '{print $1 "-" $2}'
|
|
155
|
+
fi
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
append_file_to_install_fingerprint() {
|
|
159
|
+
local relative_path="$1"
|
|
160
|
+
local source_path="$PROJECT_ROOT/$relative_path"
|
|
161
|
+
printf 'path=%s\n' "$relative_path"
|
|
162
|
+
if [ -f "$source_path" ]; then
|
|
163
|
+
cat "$source_path"
|
|
164
|
+
else
|
|
165
|
+
printf '<missing>\n'
|
|
166
|
+
fi
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
append_directory_to_install_fingerprint() {
|
|
170
|
+
local relative_path="$1"
|
|
171
|
+
local source_path="$PROJECT_ROOT/$relative_path"
|
|
172
|
+
local file
|
|
173
|
+
printf 'path=%s\n' "$relative_path"
|
|
174
|
+
if [ ! -d "$source_path" ]; then
|
|
175
|
+
printf '<missing>\n'
|
|
176
|
+
return
|
|
177
|
+
fi
|
|
178
|
+
|
|
179
|
+
while IFS= read -r file; do
|
|
180
|
+
printf 'file=%s\n' "${file#"${PROJECT_ROOT}/"}"
|
|
181
|
+
cat "$file"
|
|
182
|
+
done < <(find -P "$source_path" -type f -print | LC_ALL=C sort)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
# Everything that can change the resolved dependency tree. The leading version marker
|
|
186
|
+
# invalidates every stored state when this scheme itself changes.
|
|
187
|
+
calculate_install_fingerprint() {
|
|
188
|
+
local filename workspace argument
|
|
189
|
+
{
|
|
190
|
+
printf 'prepare-node-modules-install-state=1\n'
|
|
191
|
+
printf 'node-version=%s\n' "$(node --version)"
|
|
192
|
+
printf 'pnpm-version=%s\n' "$(pnpm --version)"
|
|
193
|
+
printf 'node-env=%s\n' "${NODE_ENV:-}"
|
|
194
|
+
printf 'npm-config-production=%s\n' "${npm_config_production:-}"
|
|
195
|
+
printf 'npm-config-production-upper=%s\n' "${NPM_CONFIG_PRODUCTION:-}"
|
|
196
|
+
printf 'pnpm-config-production=%s\n' "${PNPM_CONFIG_PRODUCTION:-}"
|
|
197
|
+
for argument in "$@"; do
|
|
198
|
+
printf 'install-argument=%s\n' "$argument"
|
|
199
|
+
done
|
|
200
|
+
for filename in package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc; do
|
|
201
|
+
append_file_to_install_fingerprint "$filename"
|
|
202
|
+
done
|
|
203
|
+
for workspace in client server; do
|
|
204
|
+
append_file_to_install_fingerprint "$workspace/package.json"
|
|
205
|
+
done
|
|
206
|
+
append_directory_to_install_fingerprint patches
|
|
207
|
+
} | hash_install_input
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
install_requires_refresh() {
|
|
211
|
+
local argument
|
|
212
|
+
for argument in "$@"; do
|
|
213
|
+
case "$argument" in
|
|
214
|
+
--force|-f|--lockfile-only|--fix-lockfile) return 0 ;;
|
|
215
|
+
esac
|
|
216
|
+
done
|
|
217
|
+
return 1
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
# Reuse skips pnpm install entirely, so it must not skip side effects that live outside
|
|
221
|
+
# node_modules. preinstall is exempt: it only guards which package manager may run, and
|
|
222
|
+
# there is no install to guard when the tree is already in place.
|
|
223
|
+
package_has_install_lifecycle() {
|
|
224
|
+
local package_path="$1"
|
|
225
|
+
node - "$package_path" <<'NODE'
|
|
226
|
+
const fs = require('node:fs');
|
|
227
|
+
const [packagePath] = process.argv.slice(2);
|
|
228
|
+
|
|
229
|
+
try {
|
|
230
|
+
const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
|
231
|
+
const scripts = packageJson && typeof packageJson.scripts === 'object' && packageJson.scripts
|
|
232
|
+
? packageJson.scripts
|
|
233
|
+
: {};
|
|
234
|
+
const hasLifecycle = ['install', 'postinstall', 'prepare'].some(
|
|
235
|
+
name => typeof scripts[name] === 'string' && scripts[name].trim() !== '',
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
process.exit(hasLifecycle ? 0 : 1);
|
|
239
|
+
} catch {
|
|
240
|
+
process.exit(0);
|
|
241
|
+
}
|
|
242
|
+
NODE
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
install_reuse_is_safe() {
|
|
246
|
+
local workspace package_path
|
|
247
|
+
for package_path in "$PROJECT_ROOT/.pnpmfile.cjs" "$PROJECT_ROOT/.pnpmfile.js"; do
|
|
248
|
+
if [ -e "$package_path" ]; then
|
|
249
|
+
echo "[node_modules] Reinstalling because pnpm hooks are present: $package_path"
|
|
250
|
+
return 1
|
|
251
|
+
fi
|
|
252
|
+
done
|
|
253
|
+
|
|
254
|
+
if [ -f "$PROJECT_ROOT/.npmrc" ] && grep -Eq '^[[:space:]]*pnpmfile[[:space:]]*=' "$PROJECT_ROOT/.npmrc"; then
|
|
255
|
+
echo "[node_modules] Reinstalling because .npmrc configures a pnpm hook."
|
|
256
|
+
return 1
|
|
257
|
+
fi
|
|
258
|
+
|
|
259
|
+
for workspace in . client server; do
|
|
260
|
+
package_path="$PROJECT_ROOT/$workspace/package.json"
|
|
261
|
+
[ -f "$package_path" ] || continue
|
|
262
|
+
if package_has_install_lifecycle "$package_path"; then
|
|
263
|
+
echo "[node_modules] Reinstalling because $package_path has an install lifecycle script."
|
|
264
|
+
return 1
|
|
265
|
+
fi
|
|
266
|
+
done
|
|
267
|
+
return 0
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
dependencies_are_ready() {
|
|
271
|
+
local workspace
|
|
272
|
+
[ -f "$WORK_DIR/node_modules/.modules.yaml" ] || return 1
|
|
273
|
+
for workspace in client server; do
|
|
274
|
+
if [ -f "$PROJECT_ROOT/$workspace/package.json" ] &&
|
|
275
|
+
[ ! -f "$WORK_DIR/$workspace/node_modules/.modules.yaml" ]; then
|
|
276
|
+
return 1
|
|
277
|
+
fi
|
|
278
|
+
done
|
|
279
|
+
return 0
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
# 判断顺序按代价从小到大排。指纹要读云盘上的 lockfile 和整个 patches/,所以放最后:只有其余
|
|
283
|
+
# 条件都满足、确实可能复用时才付这份开销。build.sh 和 prepare.sh 不设开关,第一行就返回了。
|
|
284
|
+
install_can_be_reused() {
|
|
285
|
+
local installed_fingerprint=""
|
|
286
|
+
|
|
287
|
+
[ "${COZE_REUSE_NODE_MODULES:-}" = "1" ] || return 1
|
|
288
|
+
install_requires_refresh "$@" && return 1
|
|
289
|
+
[ -f "$INSTALL_STATE_FILE" ] || return 1
|
|
290
|
+
IFS= read -r installed_fingerprint < "$INSTALL_STATE_FILE" || return 1
|
|
291
|
+
dependencies_are_ready || return 1
|
|
292
|
+
install_reuse_is_safe || return 1
|
|
293
|
+
[ "$installed_fingerprint" = "$(calculate_install_fingerprint "$@")" ] || return 1
|
|
294
|
+
return 0
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
write_install_state() {
|
|
298
|
+
local fingerprint="$1" temporary_state
|
|
299
|
+
temporary_state="$(mktemp "$WORK_DIR/.install-state.XXXXXX")"
|
|
300
|
+
printf '%s\n' "$fingerprint" > "$temporary_state"
|
|
301
|
+
mv -f "$temporary_state" "$INSTALL_STATE_FILE"
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
copy_install_metadata() {
|
|
305
|
+
local filename source_file target_file workspace
|
|
306
|
+
for filename in pnpm-lock.yaml pnpm-workspace.yaml .npmrc; do
|
|
307
|
+
source_file="$PROJECT_ROOT/$filename"
|
|
308
|
+
target_file="$WORK_DIR/$filename"
|
|
309
|
+
rm -f "$target_file"
|
|
310
|
+
if [ -f "$source_file" ]; then
|
|
311
|
+
cp "$source_file" "$target_file"
|
|
312
|
+
fi
|
|
313
|
+
done
|
|
314
|
+
|
|
315
|
+
link_install_entry "$PROJECT_ROOT/package.json" "$WORK_DIR/package.json"
|
|
316
|
+
link_install_entry "$PROJECT_ROOT/patches" "$WORK_DIR/patches"
|
|
317
|
+
link_install_entry "$PROJECT_ROOT/scripts" "$WORK_DIR/scripts"
|
|
318
|
+
|
|
319
|
+
for workspace in client server; do
|
|
320
|
+
if [ -f "$PROJECT_ROOT/$workspace/package.json" ]; then
|
|
321
|
+
mkdir -p "$WORK_DIR/$workspace"
|
|
322
|
+
link_install_entry "$PROJECT_ROOT/$workspace/package.json" "$WORK_DIR/$workspace/package.json"
|
|
323
|
+
link_install_entry "$PROJECT_ROOT/$workspace/scripts" "$WORK_DIR/$workspace/scripts"
|
|
324
|
+
else
|
|
325
|
+
rm -rf "$WORK_DIR/$workspace"
|
|
326
|
+
fi
|
|
327
|
+
done
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
link_node_modules() {
|
|
331
|
+
local source_dir="$1" target_dir="$2"
|
|
332
|
+
local current=""
|
|
333
|
+
mkdir -p "$target_dir"
|
|
334
|
+
if [ -L "$source_dir/node_modules" ]; then
|
|
335
|
+
current="$(readlink "$source_dir/node_modules")"
|
|
336
|
+
case "$current" in
|
|
337
|
+
"$NM_ROOT"/*/node_modules|"$NM_ROOT"/*/*/node_modules) ;;
|
|
338
|
+
*)
|
|
339
|
+
echo "[node_modules] refusing to replace unmanaged symlink: $source_dir/node_modules" >&2
|
|
340
|
+
exit 1
|
|
341
|
+
;;
|
|
342
|
+
esac
|
|
343
|
+
rm "$source_dir/node_modules"
|
|
344
|
+
elif [ -e "$source_dir/node_modules" ]; then
|
|
345
|
+
rm -rf "$source_dir/node_modules"
|
|
346
|
+
fi
|
|
347
|
+
ln -s "$target_dir" "$source_dir/node_modules"
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
cleanup_legacy_shadow_project
|
|
351
|
+
copy_install_metadata
|
|
352
|
+
|
|
353
|
+
if install_can_be_reused "$@"; then
|
|
354
|
+
echo "[node_modules] Reusing unchanged dependencies in $WORK_DIR"
|
|
355
|
+
else
|
|
356
|
+
# A stale state file must not survive a failed install.
|
|
357
|
+
rm -f "$INSTALL_STATE_FILE"
|
|
358
|
+
echo "[node_modules] Installing dependencies in $WORK_DIR"
|
|
359
|
+
if ! (cd "$WORK_DIR" && pnpm install "$@"); then
|
|
360
|
+
exit 1
|
|
361
|
+
fi
|
|
362
|
+
|
|
363
|
+
if [ -f "$WORK_DIR/pnpm-lock.yaml" ]; then
|
|
364
|
+
temporary_lockfile="$(mktemp "$PROJECT_ROOT/.pnpm-lock.yaml.XXXXXX")"
|
|
365
|
+
cp "$WORK_DIR/pnpm-lock.yaml" "$temporary_lockfile"
|
|
366
|
+
chmod 0644 "$temporary_lockfile"
|
|
367
|
+
mv -f "$temporary_lockfile" "$PROJECT_ROOT/pnpm-lock.yaml"
|
|
368
|
+
fi
|
|
369
|
+
|
|
370
|
+
# pnpm may have rewritten the lockfile, so record the fingerprint of the tree as it
|
|
371
|
+
# now stands. Otherwise the next run would see a changed input and reinstall.
|
|
372
|
+
write_install_state "$(calculate_install_fingerprint "$@")"
|
|
373
|
+
fi
|
|
374
|
+
|
|
375
|
+
link_node_modules "$PROJECT_ROOT" "$WORK_DIR/node_modules"
|
|
376
|
+
for workspace in client server; do
|
|
377
|
+
if [ -f "$PROJECT_ROOT/$workspace/package.json" ]; then
|
|
378
|
+
link_node_modules "$PROJECT_ROOT/$workspace" "$WORK_DIR/$workspace/node_modules"
|
|
379
|
+
fi
|
|
380
|
+
done
|
|
381
|
+
|
|
382
|
+
echo "[node_modules] Dependencies ready"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
if [ -z "${BASH_VERSION:-}" ]; then exec /usr/bin/env bash "$0" "$@"; fi
|
|
3
3
|
set -euo pipefail
|
|
4
|
-
ROOT_DIR="$(pwd)"
|
|
4
|
+
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
5
5
|
|
|
6
6
|
# ==================== 工具函数 ====================
|
|
7
7
|
info() {
|
|
@@ -32,8 +32,8 @@ info "==================== 安装 Node 依赖 ===================="
|
|
|
32
32
|
info "开始安装 Node 依赖"
|
|
33
33
|
if [ -f "$ROOT_DIR/package.json" ]; then
|
|
34
34
|
info "进入目录:$ROOT_DIR"
|
|
35
|
-
info "
|
|
36
|
-
|
|
35
|
+
info "正在执行受管依赖准备入口"
|
|
36
|
+
bash "$ROOT_DIR/.cozeproj/scripts/prepare-node-modules.sh" --registry=https://registry.npmmirror.com || error "Node 依赖安装失败"
|
|
37
37
|
else
|
|
38
38
|
warn "未找到 $ROOT_DIR/package.json 文件,请检查路径是否正确"
|
|
39
39
|
fi
|
|
@@ -6,13 +6,19 @@ import {
|
|
|
6
6
|
View,
|
|
7
7
|
TouchableWithoutFeedback,
|
|
8
8
|
Keyboard,
|
|
9
|
-
ViewStyle,
|
|
9
|
+
type ViewStyle,
|
|
10
10
|
FlatList,
|
|
11
|
+
type FlatListProps,
|
|
11
12
|
SectionList,
|
|
13
|
+
type SectionListProps,
|
|
14
|
+
type ScrollViewProps,
|
|
12
15
|
Modal,
|
|
13
16
|
} from 'react-native';
|
|
14
17
|
import { withUniwind } from 'uniwind';
|
|
15
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
useSafeAreaInsets,
|
|
20
|
+
type Edge,
|
|
21
|
+
} from 'react-native-safe-area-context';
|
|
16
22
|
import { StatusBar } from 'expo-status-bar';
|
|
17
23
|
// 引入 KeyboardAware 系列组件
|
|
18
24
|
import {
|
|
@@ -86,58 +92,92 @@ type KeyboardAwareProps = {
|
|
|
86
92
|
contentInsetBehaviorIOS: 'automatic' | 'never';
|
|
87
93
|
};
|
|
88
94
|
|
|
95
|
+
type NativeScrollableProps = Pick<
|
|
96
|
+
ScrollViewProps,
|
|
97
|
+
| 'contentContainerStyle'
|
|
98
|
+
| 'keyboardShouldPersistTaps'
|
|
99
|
+
| 'keyboardDismissMode'
|
|
100
|
+
| 'contentInsetAdjustmentBehavior'
|
|
101
|
+
>;
|
|
102
|
+
|
|
89
103
|
const KeyboardAwareScrollable = ({
|
|
90
104
|
element,
|
|
91
105
|
extraPadding,
|
|
92
106
|
contentInsetBehaviorIOS,
|
|
93
107
|
}: KeyboardAwareProps) => {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
108
|
+
const createEnhancedContentStyle = (
|
|
109
|
+
contentContainerStyle: ScrollViewProps['contentContainerStyle'],
|
|
110
|
+
) => {
|
|
111
|
+
const mergedStyle = StyleSheet.flatten(contentContainerStyle) ?? {};
|
|
112
|
+
const currentPaddingBottom =
|
|
113
|
+
typeof mergedStyle.paddingBottom === 'number'
|
|
114
|
+
? mergedStyle.paddingBottom
|
|
115
|
+
: 0;
|
|
100
116
|
|
|
101
|
-
|
|
102
|
-
|
|
117
|
+
return [
|
|
118
|
+
{
|
|
119
|
+
...mergedStyle,
|
|
120
|
+
paddingBottom: currentPaddingBottom + extraPadding,
|
|
121
|
+
},
|
|
122
|
+
];
|
|
123
|
+
};
|
|
103
124
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
125
|
+
const createKeyboardAwareProps = <Props extends NativeScrollableProps>(
|
|
126
|
+
childProps: Props,
|
|
127
|
+
) => ({
|
|
128
|
+
...childProps,
|
|
129
|
+
contentContainerStyle: createEnhancedContentStyle(
|
|
130
|
+
childProps.contentContainerStyle,
|
|
131
|
+
),
|
|
132
|
+
keyboardShouldPersistTaps:
|
|
133
|
+
childProps.keyboardShouldPersistTaps ?? 'handled',
|
|
134
|
+
keyboardDismissMode: childProps.keyboardDismissMode ?? 'on-drag',
|
|
110
135
|
enableOnAndroid: true,
|
|
111
136
|
// 类似于原代码中的 setTimeout/scrollToEnd 逻辑,这里设置额外的滚动高度确保输入框可见
|
|
112
137
|
extraHeight: 100,
|
|
113
138
|
// 禁用自带的 ScrollView 自动 inset,由外部 padding 控制
|
|
114
139
|
enableAutomaticScroll: true,
|
|
115
140
|
...(Platform.OS === 'ios'
|
|
116
|
-
? {
|
|
141
|
+
? {
|
|
142
|
+
contentInsetAdjustmentBehavior:
|
|
143
|
+
childProps.contentInsetAdjustmentBehavior ??
|
|
144
|
+
contentInsetBehaviorIOS,
|
|
145
|
+
}
|
|
117
146
|
: {}),
|
|
118
|
-
};
|
|
147
|
+
});
|
|
119
148
|
|
|
120
|
-
const t =
|
|
149
|
+
const t = element.type;
|
|
121
150
|
|
|
122
151
|
// 根据组件类型返回对应的 KeyboardAware 版本
|
|
123
152
|
// 注意:不再使用 KeyboardAvoidingView,直接替换为增强版 ScrollView
|
|
124
153
|
if (t === ScrollView) {
|
|
125
|
-
|
|
154
|
+
const childProps = element.props as ScrollViewProps;
|
|
155
|
+
return (
|
|
156
|
+
<KeyboardAwareScrollView {...createKeyboardAwareProps(childProps)} />
|
|
157
|
+
);
|
|
126
158
|
}
|
|
127
159
|
|
|
128
160
|
if (t === FlatList) {
|
|
129
|
-
|
|
161
|
+
const childProps = element.props as FlatListProps<unknown>;
|
|
162
|
+
return <KeyboardAwareFlatList {...createKeyboardAwareProps(childProps)} />;
|
|
130
163
|
}
|
|
131
164
|
|
|
132
165
|
if (t === SectionList) {
|
|
133
|
-
|
|
166
|
+
const childProps = element.props as SectionListProps<unknown>;
|
|
167
|
+
return (
|
|
168
|
+
<KeyboardAwareSectionList {...createKeyboardAwareProps(childProps)} />
|
|
169
|
+
);
|
|
134
170
|
}
|
|
135
171
|
|
|
136
172
|
// 理论上不应运行到这里,如果是非标准组件则原样返回,仅修改样式
|
|
173
|
+
const childProps = element.props as NativeScrollableProps;
|
|
137
174
|
return React.cloneElement(element, {
|
|
138
|
-
contentContainerStyle:
|
|
139
|
-
|
|
140
|
-
|
|
175
|
+
contentContainerStyle: createEnhancedContentStyle(
|
|
176
|
+
childProps.contentContainerStyle,
|
|
177
|
+
),
|
|
178
|
+
keyboardShouldPersistTaps:
|
|
179
|
+
childProps.keyboardShouldPersistTaps ?? 'handled',
|
|
180
|
+
keyboardDismissMode: childProps.keyboardDismissMode ?? 'on-drag',
|
|
141
181
|
});
|
|
142
182
|
};
|
|
143
183
|
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import React, { forwardRef, useMemo } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
type GestureResponderEvent,
|
|
4
|
+
View,
|
|
5
|
+
Text as RNText,
|
|
6
|
+
} from 'react-native';
|
|
3
7
|
import { GestureDetector } from 'react-native-gesture-handler';
|
|
4
8
|
import Animated from 'react-native-reanimated';
|
|
5
9
|
import { useThemeColor } from '../../helpers/external/hooks';
|
|
@@ -339,7 +343,7 @@ const ToastClose = forwardRef<View, ToastCloseProps>((props, ref) => {
|
|
|
339
343
|
* If hide and id are available from context, use them to hide the toast
|
|
340
344
|
* Otherwise, use the provided onPress handler
|
|
341
345
|
*/
|
|
342
|
-
const handlePress = (event:
|
|
346
|
+
const handlePress = (event: GestureResponderEvent) => {
|
|
343
347
|
if (hide && id) {
|
|
344
348
|
hide(id);
|
|
345
349
|
}
|
package/lib/__templates__/expo/client/heroui/helpers/internal/hooks/use-controllable-state.ts
CHANGED
|
@@ -108,7 +108,7 @@ function useUncontrolledState<T>({
|
|
|
108
108
|
* A custom hook that converts a callback to a ref to avoid triggering re-renders when passed as a
|
|
109
109
|
* prop or avoid re-executing effects when passed as a dependency
|
|
110
110
|
*/
|
|
111
|
-
function useCallbackRef<T extends (...args:
|
|
111
|
+
function useCallbackRef<T extends (...args: never[]) => unknown>(
|
|
112
112
|
callback: T | undefined
|
|
113
113
|
): T {
|
|
114
114
|
const callbackRef = useRef(callback);
|