xgem-cli 2.0.0-alpha.8 → 2.0.0-alpha.9
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 +42 -6
- package/bin/xgem +7 -3
- package/bin/xgem.js +1 -0
- package/lib/create.sh +226 -0
- package/lib/flutter.sh +103 -0
- package/lib/scaffold.sh +4 -4
- package/lib/version.sh +1 -1
- package/lib-win/scaffold.js +3 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
# xgem
|
|
2
2
|
|
|
3
|
-
A framework-aware automation CLI. `xgem init`
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
A framework-aware automation CLI. `xgem init` can create a brand-new project
|
|
4
|
+
(real `npm create vite`/`create-next-app`/`create-vue`/`ng new`/`flutter create`
|
|
5
|
+
scaffolding, framework-specific questions, dependency install, and launching
|
|
6
|
+
the dev server in your browser or the app on a picked device/simulator) or
|
|
7
|
+
just scaffold clean/build/dev scripts into a project you already have
|
|
8
|
+
(Flutter, Node, Python, React/Vue/Angular/Next, Go, Rust, Docker, Swift).
|
|
9
|
+
`xgem run <framework> <script>` runs those scripts, `xgem doctor` reports on
|
|
10
|
+
your environment, and `xgem git` wraps a common stage → commit → rebase-pull
|
|
11
|
+
→ push workflow.
|
|
8
12
|
|
|
9
13
|
Flutter's iOS builds go through a dedicated engine that detects whether your
|
|
10
14
|
project uses CocoaPods or Swift Package Manager (SPM), figures out the actual
|
|
@@ -59,7 +63,36 @@ Flags (any command): --yes (skip confirmations), --dry-run (show, don't apply),
|
|
|
59
63
|
|
|
60
64
|
## Supported frameworks
|
|
61
65
|
|
|
62
|
-
flutter, node, python, react, vue, angular, go, rust, docker, swift
|
|
66
|
+
flutter, node, python, react, vue, angular, next, go, rust, docker, swift
|
|
67
|
+
|
|
68
|
+
## Creating a new project
|
|
69
|
+
|
|
70
|
+
`xgem init` asks, per framework, whether to create a brand-new project or use
|
|
71
|
+
what's already in the directory:
|
|
72
|
+
|
|
73
|
+
- **react**: Vite or Create React App ("the normal version"), then TypeScript
|
|
74
|
+
or JavaScript, then current directory or a new folder.
|
|
75
|
+
- **vue** / **next**: runs the official `create-vue`/`create-next-app` wizard
|
|
76
|
+
directly, so you get their own real prompts (router, Tailwind, App Router,
|
|
77
|
+
etc.) rather than a re-implementation of them.
|
|
78
|
+
- **angular**: `ng new` (via a local Angular CLI if you have one, `npx
|
|
79
|
+
@angular/cli` otherwise).
|
|
80
|
+
- **flutter**: `flutter create`, then lists available devices/simulators
|
|
81
|
+
(falling back to available-but-not-booted emulators you can launch) and
|
|
82
|
+
runs on whichever you pick.
|
|
83
|
+
- **node/python/go/rust/docker/swift**: the ecosystem's own minimal init
|
|
84
|
+
command (`npm init`, a `.venv`, `cargo new`, `go mod init`, a starter
|
|
85
|
+
Dockerfile, `swift package init`) — no sub-wizard, since these don't have
|
|
86
|
+
an equivalent "dev server" to launch.
|
|
87
|
+
|
|
88
|
+
For the frameworks with a dev server (react/vue/angular/next), after
|
|
89
|
+
dependencies install xgem offers to start it and open your default browser —
|
|
90
|
+
it tails the server's own log for the first `http://localhost:PORT` it
|
|
91
|
+
prints rather than guessing a framework's default port, so it works even if
|
|
92
|
+
that port's already taken and a different one gets picked.
|
|
93
|
+
|
|
94
|
+
Answering "existing" (the default) skips all of this and behaves exactly
|
|
95
|
+
like before — just scaffolds `.xgem-automate/` into the current directory.
|
|
63
96
|
|
|
64
97
|
## Architecture
|
|
65
98
|
|
|
@@ -73,6 +106,7 @@ lib/
|
|
|
73
106
|
flutter.sh flutter command group (build/hard-clean/build-runner), delegates iOS builds to ios.sh
|
|
74
107
|
git.sh git cmt/init/branch/rm-remote/rm-branch
|
|
75
108
|
scaffold.sh generic clean/build/dev handling for the other, simpler frameworks
|
|
109
|
+
create.sh project creation wizards (react/vue/angular/next/simple frameworks)
|
|
76
110
|
templates/ the actual clean/build/dev script content xgem scaffolds into your project's .xgem-automate/
|
|
77
111
|
```
|
|
78
112
|
|
|
@@ -134,6 +168,8 @@ Deferred to a follow-up pass, not yet in this release:
|
|
|
134
168
|
- Shell completions, `--json` output, per-project config caching (stop re-prompting
|
|
135
169
|
build name/number/platform every run)
|
|
136
170
|
- Deeper Android SDK/Java detection
|
|
171
|
+
- Project creation wizards on the Windows engine (`lib-win/`) — react/vue/angular/next
|
|
172
|
+
scaffolding is pure Node.js tooling and would work fine there too, just not built yet
|
|
137
173
|
|
|
138
174
|
## License
|
|
139
175
|
|
package/bin/xgem
CHANGED
|
@@ -33,6 +33,8 @@ source "$XGEM_HOME/lib/ios.sh"
|
|
|
33
33
|
source "$XGEM_HOME/lib/flutter.sh"
|
|
34
34
|
# shellcheck source=lib/scaffold.sh
|
|
35
35
|
source "$XGEM_HOME/lib/scaffold.sh"
|
|
36
|
+
# shellcheck source=lib/create.sh
|
|
37
|
+
source "$XGEM_HOME/lib/create.sh"
|
|
36
38
|
|
|
37
39
|
CONFIG_DIR=".xgem-automate"
|
|
38
40
|
|
|
@@ -85,12 +87,12 @@ case "${1:-}" in
|
|
|
85
87
|
log_warn "Workspace configuration layer already exists! Use 'xgem add' to append frameworks."
|
|
86
88
|
exit 0
|
|
87
89
|
fi
|
|
88
|
-
mkdir -p "$CONFIG_DIR"
|
|
89
|
-
|
|
90
90
|
echo -e "\033[1;34mSelect your starting framework architecture:\033[0m"
|
|
91
91
|
PS3="Enter target selection number: "
|
|
92
92
|
select fw in "${ALL_FRAMEWORKS[@]}"; do
|
|
93
93
|
if [ -n "$fw" ]; then
|
|
94
|
+
create_project_wizard "$fw"
|
|
95
|
+
mkdir -p "$CONFIG_DIR"
|
|
94
96
|
scaffold_inject_templates "$fw" "$CONFIG_DIR"
|
|
95
97
|
log_success "Successfully appended standard scripts for: $CONFIG_DIR/$fw"
|
|
96
98
|
break
|
|
@@ -108,6 +110,8 @@ case "${1:-}" in
|
|
|
108
110
|
echo "$CONFIG_DIR/" > .gitignore
|
|
109
111
|
log_success "Created .gitignore and hidden tracking layer folder references."
|
|
110
112
|
fi
|
|
113
|
+
|
|
114
|
+
_ask_launch_dev_server
|
|
111
115
|
exit 0
|
|
112
116
|
;;
|
|
113
117
|
|
|
@@ -198,7 +202,7 @@ case "${1:-}" in
|
|
|
198
202
|
exit 0
|
|
199
203
|
;;
|
|
200
204
|
|
|
201
|
-
flutter|node|python|react|vue|angular|go|rust|docker|swift)
|
|
205
|
+
flutter|node|python|react|vue|angular|next|go|rust|docker|swift)
|
|
202
206
|
TARGET_FW=$1
|
|
203
207
|
TARGET_FW_UPPER=$(echo "$TARGET_FW" | tr '[:lower:]' '[:upper:]')
|
|
204
208
|
|
package/bin/xgem.js
CHANGED
package/lib/create.sh
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# xgem project creation wizards — runs from `xgem init`, before
|
|
3
|
+
# scaffold_inject_templates, for frameworks with a real "ask sub-choice ->
|
|
4
|
+
# scaffold -> install -> launch" story worth building (react/vue/angular/
|
|
5
|
+
# next/flutter). The rest (node/python/go/rust/docker/swift) get a lighter
|
|
6
|
+
# "create with the ecosystem's own standard init command" step.
|
|
7
|
+
#
|
|
8
|
+
# Depends on lib/logger.sh, lib/utils.sh. Uses `cd` directly (no
|
|
9
|
+
# subshells) so a newly created project directory persists as the cwd for
|
|
10
|
+
# the rest of `xgem init` (template injection, .gitignore) without the
|
|
11
|
+
# caller needing to track/re-cd into a returned path.
|
|
12
|
+
#
|
|
13
|
+
# Dev-server launch is deliberately deferred: creators only set
|
|
14
|
+
# XGEM_POST_INIT_LAUNCH_CMD (an array) rather than launching immediately,
|
|
15
|
+
# so `xgem init` finishes template injection and .gitignore setup *before*
|
|
16
|
+
# handing the terminal over to a long-running dev server.
|
|
17
|
+
XGEM_POST_INIT_LAUNCH_CMD=()
|
|
18
|
+
|
|
19
|
+
# _prompt_project_location <human label> -> echoes "." or a new folder name.
|
|
20
|
+
# Pure prompt-and-echo, no other stdout output, so it's safe to capture via
|
|
21
|
+
# command substitution.
|
|
22
|
+
_prompt_project_location() {
|
|
23
|
+
local label=$1
|
|
24
|
+
local choice
|
|
25
|
+
read -r -p "Initialize $label in the current directory, or create a new folder? [current/new] (default: new): " choice
|
|
26
|
+
choice=${choice:-new}
|
|
27
|
+
if [[ "$choice" == c* || "$choice" == C* ]]; then
|
|
28
|
+
echo "."
|
|
29
|
+
return 0
|
|
30
|
+
fi
|
|
31
|
+
local name
|
|
32
|
+
read -r -p "Project name: " name
|
|
33
|
+
[ -n "$name" ] || die "Project name cannot be empty."
|
|
34
|
+
echo "$name"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
_open_url() {
|
|
38
|
+
local url=$1
|
|
39
|
+
case "$(detect_os)" in
|
|
40
|
+
darwin) open "$url" 2>/dev/null ;;
|
|
41
|
+
linux) xdg-open "$url" 2>/dev/null ;;
|
|
42
|
+
*) log_debug "Don't know how to open a browser on this OS — visit $url manually." ;;
|
|
43
|
+
esac
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# _launch_dev_server_and_open_browser <cmd...>
|
|
47
|
+
# Starts the dev server in the background, tails its log for the first
|
|
48
|
+
# http://localhost URL it prints — this works across Vite/CRA/Next/Angular
|
|
49
|
+
# without hardcoding any one framework's default port (which can differ or
|
|
50
|
+
# be taken already) — opens it, then waits so Ctrl-C stops the server
|
|
51
|
+
# normally instead of leaving it orphaned in the background.
|
|
52
|
+
_launch_dev_server_and_open_browser() {
|
|
53
|
+
local log_file
|
|
54
|
+
log_file=$(mktemp)
|
|
55
|
+
log_info "Starting dev server: $*"
|
|
56
|
+
"$@" > "$log_file" 2>&1 &
|
|
57
|
+
local server_pid=$!
|
|
58
|
+
|
|
59
|
+
local url="" waited=0
|
|
60
|
+
while [ -z "$url" ] && [ "$waited" -lt 30 ] && kill -0 "$server_pid" 2>/dev/null; do
|
|
61
|
+
url=$(grep -oE 'https?://(localhost|127\.0\.0\.1)[:0-9]*[^[:space:]]*' "$log_file" 2>/dev/null | head -1)
|
|
62
|
+
if [ -z "$url" ]; then
|
|
63
|
+
sleep 1
|
|
64
|
+
waited=$((waited + 1))
|
|
65
|
+
fi
|
|
66
|
+
done
|
|
67
|
+
|
|
68
|
+
if [ -n "$url" ]; then
|
|
69
|
+
log_success "Dev server up at $url"
|
|
70
|
+
_open_url "$url"
|
|
71
|
+
elif kill -0 "$server_pid" 2>/dev/null; then
|
|
72
|
+
log_warn "Dev server is running (pid $server_pid) but its URL wasn't detected automatically after ${waited}s — check the output below."
|
|
73
|
+
else
|
|
74
|
+
log_error "Dev server exited early."
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
cat "$log_file" &
|
|
78
|
+
wait "$server_pid" 2>/dev/null
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
_ask_launch_dev_server() {
|
|
82
|
+
[ ${#XGEM_POST_INIT_LAUNCH_CMD[@]} -gt 0 ] || return 0
|
|
83
|
+
local answer
|
|
84
|
+
read -r -p "Launch the dev server now and open it in your browser? (Y/n): " answer
|
|
85
|
+
answer=${answer:-y}
|
|
86
|
+
[[ "$answer" == "y" || "$answer" == "Y" ]] && _launch_dev_server_and_open_browser "${XGEM_POST_INIT_LAUNCH_CMD[@]}"
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
_create_react() {
|
|
90
|
+
local variant lang project_dir
|
|
91
|
+
read -r -p "Use Vite or Create React App (the 'normal' version)? [vite/cra] (default: vite): " variant
|
|
92
|
+
variant=${variant:-vite}
|
|
93
|
+
read -r -p "TypeScript or JavaScript? [ts/js] (default: ts): " lang
|
|
94
|
+
lang=${lang:-ts}
|
|
95
|
+
project_dir=$(_prompt_project_location "React")
|
|
96
|
+
|
|
97
|
+
if [[ "$variant" == cra* || "$variant" == CRA* ]]; then
|
|
98
|
+
require_cmd npx "Install Node.js (npm ships with it): https://nodejs.org"
|
|
99
|
+
local -a args=(create-react-app)
|
|
100
|
+
[ "$project_dir" != "." ] && args+=("$project_dir") || args+=(.)
|
|
101
|
+
[ "$lang" = "ts" ] && args+=(--template typescript)
|
|
102
|
+
npx "${args[@]}" || die "create-react-app failed."
|
|
103
|
+
else
|
|
104
|
+
require_cmd npm "Install Node.js (npm ships with it): https://nodejs.org"
|
|
105
|
+
local template="react"
|
|
106
|
+
[ "$lang" = "ts" ] && template="react-ts"
|
|
107
|
+
npm create vite@latest "$project_dir" -- --template "$template" || die "npm create vite failed."
|
|
108
|
+
fi
|
|
109
|
+
|
|
110
|
+
[ "$project_dir" != "." ] && { cd "$project_dir" || die "Could not enter $project_dir"; }
|
|
111
|
+
log_info "Installing dependencies..."
|
|
112
|
+
npm install
|
|
113
|
+
XGEM_POST_INIT_LAUNCH_CMD=(npm run dev)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
_create_vue() {
|
|
117
|
+
require_cmd npm "Install Node.js (npm ships with it): https://nodejs.org"
|
|
118
|
+
local project_dir
|
|
119
|
+
project_dir=$(_prompt_project_location "Vue")
|
|
120
|
+
npm create vue@latest "$project_dir" || die "npm create vue failed."
|
|
121
|
+
[ "$project_dir" != "." ] && { cd "$project_dir" || die "Could not enter $project_dir"; }
|
|
122
|
+
log_info "Installing dependencies..."
|
|
123
|
+
npm install
|
|
124
|
+
XGEM_POST_INIT_LAUNCH_CMD=(npm run dev)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
_create_angular() {
|
|
128
|
+
local project_dir
|
|
129
|
+
project_dir=$(_prompt_project_location "Angular")
|
|
130
|
+
local -a ng_cmd=(ng)
|
|
131
|
+
has_cmd ng || ng_cmd=(npx @angular/cli@latest)
|
|
132
|
+
|
|
133
|
+
if [ "$project_dir" = "." ]; then
|
|
134
|
+
"${ng_cmd[@]}" new "$(basename "$PWD")" --directory=. || die "ng new failed."
|
|
135
|
+
else
|
|
136
|
+
"${ng_cmd[@]}" new "$project_dir" || die "ng new failed."
|
|
137
|
+
cd "$project_dir" || die "Could not enter $project_dir"
|
|
138
|
+
fi
|
|
139
|
+
XGEM_POST_INIT_LAUNCH_CMD=(npm run start)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
_create_next() {
|
|
143
|
+
require_cmd npx "Install Node.js (npm ships with it): https://nodejs.org"
|
|
144
|
+
local project_dir
|
|
145
|
+
project_dir=$(_prompt_project_location "Next.js")
|
|
146
|
+
npx create-next-app@latest "$project_dir" || die "create-next-app failed."
|
|
147
|
+
[ "$project_dir" != "." ] && { cd "$project_dir" || die "Could not enter $project_dir"; }
|
|
148
|
+
XGEM_POST_INIT_LAUNCH_CMD=(npm run dev)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
# Lighter path for frameworks without a dev-server/browser story — just the
|
|
152
|
+
# ecosystem's own standard init command, no sub-wizard.
|
|
153
|
+
_create_simple() {
|
|
154
|
+
local fw=$1
|
|
155
|
+
local project_dir
|
|
156
|
+
project_dir=$(_prompt_project_location "$fw")
|
|
157
|
+
[ "$project_dir" != "." ] && { mkdir -p "$project_dir" && cd "$project_dir" || die "Could not enter $project_dir"; }
|
|
158
|
+
|
|
159
|
+
case "$fw" in
|
|
160
|
+
node)
|
|
161
|
+
require_cmd npm "Install Node.js (npm ships with it): https://nodejs.org"
|
|
162
|
+
npm init -y
|
|
163
|
+
;;
|
|
164
|
+
python)
|
|
165
|
+
require_cmd python3 "Install Python 3: https://www.python.org/downloads/"
|
|
166
|
+
python3 -m venv .venv
|
|
167
|
+
log_success "Created .venv — activate with 'source .venv/bin/activate'."
|
|
168
|
+
;;
|
|
169
|
+
go)
|
|
170
|
+
require_cmd go "Install Go: https://go.dev/doc/install"
|
|
171
|
+
local module_name
|
|
172
|
+
read -r -p "Module name (e.g. github.com/you/project): " module_name
|
|
173
|
+
[ -n "$module_name" ] || module_name=$(basename "$PWD")
|
|
174
|
+
go mod init "$module_name"
|
|
175
|
+
;;
|
|
176
|
+
rust)
|
|
177
|
+
require_cmd cargo "Install Rust: https://www.rust-lang.org/tools/install"
|
|
178
|
+
if [ "$project_dir" != "." ]; then
|
|
179
|
+
cd .. || return 1
|
|
180
|
+
cargo new "$project_dir" || die "cargo new failed."
|
|
181
|
+
cd "$project_dir" || die "Could not enter $project_dir"
|
|
182
|
+
else
|
|
183
|
+
cargo init || die "cargo init failed."
|
|
184
|
+
fi
|
|
185
|
+
;;
|
|
186
|
+
swift)
|
|
187
|
+
require_cmd swift "Install Swift: https://www.swift.org/install/"
|
|
188
|
+
swift package init --type executable
|
|
189
|
+
;;
|
|
190
|
+
docker)
|
|
191
|
+
if [ ! -f Dockerfile ]; then
|
|
192
|
+
cat > Dockerfile << 'EOF'
|
|
193
|
+
FROM alpine:latest
|
|
194
|
+
WORKDIR /app
|
|
195
|
+
COPY . .
|
|
196
|
+
CMD ["sh"]
|
|
197
|
+
EOF
|
|
198
|
+
log_success "Created a starter Dockerfile — edit it for your actual base image/entrypoint."
|
|
199
|
+
else
|
|
200
|
+
log_warn "Dockerfile already exists, leaving it as-is."
|
|
201
|
+
fi
|
|
202
|
+
;;
|
|
203
|
+
esac
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
# create_project_wizard <framework>
|
|
207
|
+
# Asks whether to create a new project or use what's already here; if
|
|
208
|
+
# creating, dispatches to the right per-framework creator. Leaves the
|
|
209
|
+
# process cwd inside the (possibly new) project directory either way.
|
|
210
|
+
create_project_wizard() {
|
|
211
|
+
local fw=$1
|
|
212
|
+
local create_choice
|
|
213
|
+
read -r -p "Create a brand-new $fw project, or use what's already in this directory? [new/existing] (default: existing): " create_choice
|
|
214
|
+
create_choice=${create_choice:-existing}
|
|
215
|
+
[[ "$create_choice" == n* || "$create_choice" == N* ]] || return 0
|
|
216
|
+
|
|
217
|
+
case "$fw" in
|
|
218
|
+
react) _create_react ;;
|
|
219
|
+
vue) _create_vue ;;
|
|
220
|
+
angular) _create_angular ;;
|
|
221
|
+
next) _create_next ;;
|
|
222
|
+
flutter) create_flutter_project ;;
|
|
223
|
+
node|python|go|rust|docker|swift) _create_simple "$fw" ;;
|
|
224
|
+
*) log_debug "No creation wizard for '$fw'; using current directory as-is." ;;
|
|
225
|
+
esac
|
|
226
|
+
}
|
package/lib/flutter.sh
CHANGED
|
@@ -237,3 +237,106 @@ flutter_run_native_command() {
|
|
|
237
237
|
*) die "No native flutter command for '$script'." ;;
|
|
238
238
|
esac
|
|
239
239
|
}
|
|
240
|
+
|
|
241
|
+
# create_flutter_project — used by lib/create.sh's create_project_wizard.
|
|
242
|
+
# `flutter create` needs a target dir; "." scaffolds into the current one.
|
|
243
|
+
create_flutter_project() {
|
|
244
|
+
require_cmd flutter "Install Flutter: https://docs.flutter.dev/get-started/install"
|
|
245
|
+
local project_dir
|
|
246
|
+
project_dir=$(_prompt_project_location "Flutter")
|
|
247
|
+
if [ "$project_dir" = "." ]; then
|
|
248
|
+
flutter create . || die "flutter create failed."
|
|
249
|
+
else
|
|
250
|
+
flutter create "$project_dir" || die "flutter create failed."
|
|
251
|
+
cd "$project_dir" || die "Could not enter $project_dir"
|
|
252
|
+
fi
|
|
253
|
+
_flutter_offer_run_on_device
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
# _flutter_select_device -> echoes the chosen device id, or nothing if
|
|
257
|
+
# none available/selected. Uses `flutter devices --machine` (JSON) parsed
|
|
258
|
+
# with python3 — already a soft dependency elsewhere in lib/ios.sh for the
|
|
259
|
+
# same reason (no jq assumed present).
|
|
260
|
+
_flutter_select_device() {
|
|
261
|
+
require_cmd python3 "Needed to parse 'flutter devices --machine' output."
|
|
262
|
+
local devices_json_file
|
|
263
|
+
devices_json_file=$(mktemp)
|
|
264
|
+
flutter devices --machine > "$devices_json_file" 2>/dev/null
|
|
265
|
+
|
|
266
|
+
local -a ids=() labels=()
|
|
267
|
+
while IFS='|' read -r id label; do
|
|
268
|
+
[ -n "$id" ] || continue
|
|
269
|
+
ids+=("$id")
|
|
270
|
+
labels+=("$label")
|
|
271
|
+
done < <(python3 - "$devices_json_file" <<'PYEOF'
|
|
272
|
+
import json, sys
|
|
273
|
+
|
|
274
|
+
try:
|
|
275
|
+
with open(sys.argv[1]) as f:
|
|
276
|
+
devices = json.load(f)
|
|
277
|
+
except Exception:
|
|
278
|
+
devices = []
|
|
279
|
+
|
|
280
|
+
for d in devices:
|
|
281
|
+
platform = d.get("platform") or d.get("targetPlatform") or ""
|
|
282
|
+
name = d.get("name", "unknown")
|
|
283
|
+
device_id = d.get("id", "")
|
|
284
|
+
print(f"{device_id}|{name} ({platform})")
|
|
285
|
+
PYEOF
|
|
286
|
+
)
|
|
287
|
+
rm -f "$devices_json_file"
|
|
288
|
+
|
|
289
|
+
if [ ${#ids[@]} -eq 0 ]; then
|
|
290
|
+
log_warn "No running devices/simulators found."
|
|
291
|
+
_flutter_offer_launch_emulator
|
|
292
|
+
return 1
|
|
293
|
+
fi
|
|
294
|
+
|
|
295
|
+
echo "Available devices:" >&2
|
|
296
|
+
local i
|
|
297
|
+
for i in "${!ids[@]}"; do
|
|
298
|
+
echo "$((i + 1))) ${labels[$i]}" >&2
|
|
299
|
+
done
|
|
300
|
+
|
|
301
|
+
local choice
|
|
302
|
+
read -r -p "Select a device [1-${#ids[@]}]: " choice
|
|
303
|
+
if ! [[ "$choice" =~ ^[0-9]+$ ]] || [ "$choice" -lt 1 ] || [ "$choice" -gt "${#ids[@]}" ]; then
|
|
304
|
+
log_warn "Invalid selection."
|
|
305
|
+
return 1
|
|
306
|
+
fi
|
|
307
|
+
echo "${ids[$((choice - 1))]}"
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
# No running devices — check for available-but-not-booted emulators and
|
|
311
|
+
# offer to launch one, matching the "check available simulators... then
|
|
312
|
+
# launch it" behavior the user asked for.
|
|
313
|
+
_flutter_offer_launch_emulator() {
|
|
314
|
+
local emulators_output
|
|
315
|
+
emulators_output=$(flutter emulators 2>/dev/null)
|
|
316
|
+
[ -n "$emulators_output" ] || return 1
|
|
317
|
+
|
|
318
|
+
echo "$emulators_output"
|
|
319
|
+
local launch_choice
|
|
320
|
+
read -r -p "Launch one of these emulators? Enter its id, or leave blank to skip: " launch_choice
|
|
321
|
+
[ -n "$launch_choice" ] || return 1
|
|
322
|
+
|
|
323
|
+
log_info "Launching emulator '$launch_choice'..."
|
|
324
|
+
flutter emulators --launch "$launch_choice"
|
|
325
|
+
log_info "Waiting for it to boot..."
|
|
326
|
+
sleep 5
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
_flutter_offer_run_on_device() {
|
|
330
|
+
local run_choice
|
|
331
|
+
read -r -p "Run the app now on a device/simulator? (Y/n): " run_choice
|
|
332
|
+
run_choice=${run_choice:-y}
|
|
333
|
+
[[ "$run_choice" == "y" || "$run_choice" == "Y" ]] || return 0
|
|
334
|
+
|
|
335
|
+
local device_id
|
|
336
|
+
device_id=$(_flutter_select_device)
|
|
337
|
+
if [ -n "$device_id" ]; then
|
|
338
|
+
flutter run -d "$device_id"
|
|
339
|
+
else
|
|
340
|
+
log_warn "No device selected — run 'flutter run' manually once one is available."
|
|
341
|
+
fi
|
|
342
|
+
}
|
package/lib/scaffold.sh
CHANGED
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
#
|
|
12
12
|
# Depends on lib/logger.sh and XGEM_HOME (set by bin/xgem).
|
|
13
13
|
|
|
14
|
-
ALL_FRAMEWORKS=(flutter node python react vue angular go rust docker swift)
|
|
14
|
+
ALL_FRAMEWORKS=(flutter node python react vue angular next go rust docker swift)
|
|
15
15
|
|
|
16
16
|
framework_scripts() {
|
|
17
17
|
case "$1" in
|
|
18
18
|
flutter) echo "hard-clean build build-runner" ;;
|
|
19
19
|
node) echo "hard-clean build start" ;;
|
|
20
20
|
python) echo "hard-clean install" ;;
|
|
21
|
-
react|vue|angular) echo "hard-clean build dev" ;;
|
|
21
|
+
react|vue|angular|next) echo "hard-clean build dev" ;;
|
|
22
22
|
go) echo "hard-clean build" ;;
|
|
23
23
|
rust) echo "hard-clean build" ;;
|
|
24
24
|
docker) echo "hard-clean build-up" ;;
|
|
@@ -30,8 +30,8 @@ framework_scripts() {
|
|
|
30
30
|
_scaffold_template_dir() {
|
|
31
31
|
local framework=$1
|
|
32
32
|
case "$framework" in
|
|
33
|
-
react|vue|angular) echo "$XGEM_HOME/templates/webframework" ;;
|
|
34
|
-
*)
|
|
33
|
+
react|vue|angular|next) echo "$XGEM_HOME/templates/webframework" ;;
|
|
34
|
+
*) echo "$XGEM_HOME/templates/$framework" ;;
|
|
35
35
|
esac
|
|
36
36
|
}
|
|
37
37
|
|
package/lib/version.sh
CHANGED
package/lib-win/scaffold.js
CHANGED
|
@@ -7,7 +7,7 @@ const path = require('node:path');
|
|
|
7
7
|
const { spawnSync } = require('node:child_process');
|
|
8
8
|
const { logInfo, logSuccess, logWarn, logError, die } = require('./logger');
|
|
9
9
|
|
|
10
|
-
const ALL_FRAMEWORKS = ['flutter', 'node', 'python', 'react', 'vue', 'angular', 'go', 'rust', 'docker'];
|
|
10
|
+
const ALL_FRAMEWORKS = ['flutter', 'node', 'python', 'react', 'vue', 'angular', 'next', 'go', 'rust', 'docker'];
|
|
11
11
|
|
|
12
12
|
const FRAMEWORK_SCRIPTS = {
|
|
13
13
|
flutter: ['hard-clean', 'build', 'build-runner'],
|
|
@@ -16,6 +16,7 @@ const FRAMEWORK_SCRIPTS = {
|
|
|
16
16
|
react: ['hard-clean', 'build', 'dev'],
|
|
17
17
|
vue: ['hard-clean', 'build', 'dev'],
|
|
18
18
|
angular: ['hard-clean', 'build', 'dev'],
|
|
19
|
+
next: ['hard-clean', 'build', 'dev'],
|
|
19
20
|
go: ['hard-clean', 'build'],
|
|
20
21
|
rust: ['hard-clean', 'build'],
|
|
21
22
|
docker: ['hard-clean', 'build-up'],
|
|
@@ -27,7 +28,7 @@ function frameworkScripts(fw) {
|
|
|
27
28
|
|
|
28
29
|
function templateDir(fw) {
|
|
29
30
|
const templatesRoot = path.join(__dirname, '..', 'templates-win');
|
|
30
|
-
if (fw === 'react' || fw === 'vue' || fw === 'angular') {
|
|
31
|
+
if (fw === 'react' || fw === 'vue' || fw === 'angular' || fw === 'next') {
|
|
31
32
|
return path.join(templatesRoot, 'webframework');
|
|
32
33
|
}
|
|
33
34
|
return path.join(templatesRoot, fw);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xgem-cli",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.9",
|
|
4
4
|
"description": "Framework-aware automation CLI: scaffolds and runs clean/build/dev scripts per project type, an environment doctor, a SwiftPM-aware iOS build engine, and a git workflow helper. Runs natively on macOS, Linux, and Windows.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"xgem": "bin/xgem.js"
|