Skip to content

Commit 183f11a

Browse files
committed
Improve error messages with paths and actionable hints
Reworks cli_abort/cli_warn calls so users see the offending value and a specific fix: config validator names the bad field and file to edit, cache_remove shows an example invocation, multi-app field errors include an example apps: list, platform detection and backend resolver list valid values, run.R electron-script errors show the full path and a snippet, brand.yml parse error names the file, install.R lists supported archive formats, nodejs checksum warning names the file.
1 parent 51650d7 commit 183f11a

8 files changed

Lines changed: 72 additions & 15 deletions

File tree

R/backend-resolve.R

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ resolve_backend_module <- function(app_type, runtime_strategy) {
1111
if (grepl("^r-", app_type)) "native-r.js" else "native-py.js"
1212
},
1313
"container" = "container.js",
14-
cli::cli_abort("Unknown runtime strategy: {.val {runtime_strategy}}")
14+
cli::cli_abort(c(
15+
"Unknown runtime strategy: {.val {runtime_strategy}}",
16+
"i" = "Valid strategies: {.val {c('system', 'bundled', 'auto-download', 'container', 'shinylive')}}",
17+
"i" = "Set in {.arg runtime_strategy} or {.field build.runtime_strategy} in {.file _shinyelectron.yml}"
18+
))
1519
)
1620
}
1721

R/cache.R

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,11 @@ cache_remove <- function(runtime, version, platform = NULL, arch = NULL) {
266266
target <- fs::path(dir, "nodejs", version)
267267
} else {
268268
if (is.null(platform) || is.null(arch)) {
269-
cli::cli_abort("Both {.arg platform} and {.arg arch} are required for {.val {runtime}}")
269+
cli::cli_abort(c(
270+
"Both {.arg platform} and {.arg arch} are required to remove {.val {runtime}}",
271+
"i" = "Example: {.code cache_remove(\"{runtime}\", \"{version}\", \"mac\", \"arm64\")}",
272+
"i" = "Run {.code cache_info()} to list available versions"
273+
))
270274
}
271275
target <- fs::path(dir, runtime, platform, arch, version)
272276
}

R/config.R

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -189,27 +189,42 @@ validate_config <- function(config) {
189189
default_height <- SHINYELECTRON_DEFAULTS$window_height
190190

191191
if (!is.null(config$window$width) && (!is.numeric(config$window$width) || config$window$width < 100)) {
192-
cli::cli_warn("Invalid window width in config, using default: {default_width}")
192+
cli::cli_warn(c(
193+
"Invalid {.field window.width} in config: {.val {config$window$width}}",
194+
"i" = "Must be a number >= 100; using default: {.val {default_width}}",
195+
"i" = "Edit {.field window.width} in {.file _shinyelectron.yml}"
196+
))
193197
config$window$width <- default_width
194198
}
195199
if (!is.null(config$window$height) && (!is.numeric(config$window$height) || config$window$height < 100)) {
196-
cli::cli_warn("Invalid window height in config, using default: {default_height}")
200+
cli::cli_warn(c(
201+
"Invalid {.field window.height} in config: {.val {config$window$height}}",
202+
"i" = "Must be a number >= 100; using default: {.val {default_height}}",
203+
"i" = "Edit {.field window.height} in {.file _shinyelectron.yml}"
204+
))
197205
config$window$height <- default_height
198206
}
199207

200208
# Validate port using centralized default
201209
default_port <- SHINYELECTRON_DEFAULTS$server_port
202210
if (!is.null(config$server$port)) {
203211
if (!is.numeric(config$server$port) || config$server$port < 1 || config$server$port > 65535) {
204-
cli::cli_warn("Invalid port in config, using default: {default_port}")
212+
cli::cli_warn(c(
213+
"Invalid {.field server.port} in config: {.val {config$server$port}}",
214+
"i" = "Must be an integer between 1 and 65535; using default: {.val {default_port}}",
215+
"i" = "Edit {.field server.port} in {.file _shinyelectron.yml}"
216+
))
205217
config$server$port <- default_port
206218
}
207219
}
208220

209221
# Validate splash settings
210222
if (!is.null(config$splash)) {
211223
if (!is.null(config$splash$duration) && (!is.numeric(config$splash$duration) || config$splash$duration < 0)) {
212-
cli::cli_warn("Invalid splash duration, using default: 3000")
224+
cli::cli_warn(c(
225+
"Invalid {.field splash.duration} in config: {.val {config$splash$duration}}",
226+
"i" = "Must be a non-negative number (milliseconds); using default: {.val {SHINYELECTRON_DEFAULTS$splash$duration}}"
227+
))
213228
config$splash$duration <- SHINYELECTRON_DEFAULTS$splash$duration
214229
}
215230
}
@@ -614,7 +629,12 @@ read_brand_yml <- function(appdir) {
614629
tryCatch(
615630
yaml::read_yaml(brand_file),
616631
error = function(e) {
617-
cli::cli_warn(c("Failed to parse {.file _brand.yml}", "x" = "Error: {e$message}", "i" = "Using default branding"))
632+
cli::cli_warn(c(
633+
"Failed to parse {.file {brand_file}}",
634+
"x" = "{e$message}",
635+
"i" = "Check YAML syntax and indentation",
636+
"i" = "Using default branding"
637+
))
618638
NULL
619639
}
620640
)

R/install-nodejs.R

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,11 @@ nodejs_verify_checksum <- function(file_path, expected_checksum) {
123123
actual_checksum <- compute_sha256(file_path)
124124

125125
if (is.null(actual_checksum)) {
126-
cli::cli_warn("Could not verify checksum")
126+
cli::cli_warn(c(
127+
"Could not compute SHA-256 for {.file {basename(file_path)}}",
128+
"i" = "Integrity check skipped; install continuing",
129+
"i" = "If the install behaves oddly, remove {.path {dirname(file_path)}} and retry"
130+
))
127131
return(TRUE) # Don't fail if verification unavailable
128132
}
129133

R/install.R

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,10 @@ download_and_extract_portable_tool <- function(label, version, install_path,
6161
} else if (ext == "zip") {
6262
utils::unzip(temp_file, exdir = install_path)
6363
} else {
64-
cli::cli_abort("Unsupported archive extension: {.val {ext}}")
64+
cli::cli_abort(c(
65+
"Unsupported archive extension: {.val {ext}}",
66+
"i" = "Supported formats: {.val {c('gz', 'zip')}}"
67+
))
6568
}
6669
}, error = function(e) {
6770
unlink(install_path, recursive = TRUE)

R/run.R

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,19 @@ run_electron_app <- function(app_dir, port = 3000, open_devtools = TRUE, verbose
5454
# Check if package.json exists and has necessary scripts
5555
package_json_path <- fs::path(app_dir, "package.json")
5656
if (!fs::file_exists(package_json_path)) {
57-
cli::cli_abort("No package.json found in: {.path {app_dir}}")
57+
cli::cli_abort(c(
58+
"No {.file package.json} found in {.path {app_dir}}",
59+
"i" = "Run {.code export()} first to build the Electron project"
60+
))
5861
}
5962

6063
package_json <- jsonlite::fromJSON(package_json_path, simplifyVector = FALSE)
6164
if (is.null(package_json$scripts$electron)) {
62-
cli::cli_abort("No 'electron' script found in package.json")
65+
cli::cli_abort(c(
66+
"No {.val electron} script in {.file {package_json_path}}",
67+
"i" = "Add one under {.field scripts}: {.code \"electron\": \"electron .\"}",
68+
"i" = "Or re-run {.code export()} to regenerate {.file package.json}"
69+
))
6370
}
6471

6572
# Set environment variables

R/utils.R

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ detect_current_platform <- function() {
88
"Windows" = "win",
99
"Darwin" = "mac",
1010
"Linux" = "linux",
11-
cli::cli_abort("Unsupported platform: {sysname}")
11+
cli::cli_abort(c(
12+
"Unsupported platform: {.val {sysname}}",
13+
"i" = "shinyelectron supports Windows, macOS, and Linux",
14+
"i" = "Report this at {.url https://github.com/coatless-rpkg/shinyelectron/issues}"
15+
))
1216
)
1317
}
1418

R/validate-app-type.R

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,16 +88,27 @@ validate_multi_app_config <- function(config, basedir) {
8888
apps <- config$apps
8989

9090
# Check for required fields
91+
example_hint <- "Example: {.code apps: [{ id: 'dashboard', name: 'Dashboard', path: './src/dashboard' }]}"
9192
for (i in seq_along(apps)) {
9293
app <- apps[[i]]
9394
if (is.null(app$id) || !nzchar(app$id)) {
94-
cli::cli_abort("App entry {i} is missing required {.field id}")
95+
cli::cli_abort(c(
96+
"Multi-app config entry {i} is missing required {.field id}",
97+
"i" = "Each app must have: {.field id}, {.field name}, {.field path}",
98+
"i" = example_hint
99+
))
95100
}
96101
if (is.null(app$name) || !nzchar(app$name)) {
97-
cli::cli_abort("App entry {i} ({app$id}) is missing required {.field name}")
102+
cli::cli_abort(c(
103+
"Multi-app config entry {i} ({.val {app$id}}) is missing required {.field name}",
104+
"i" = example_hint
105+
))
98106
}
99107
if (is.null(app$path) || !nzchar(app$path)) {
100-
cli::cli_abort("App entry {i} ({app$id}) is missing required {.field path}")
108+
cli::cli_abort(c(
109+
"Multi-app config entry {i} ({.val {app$id}}) is missing required {.field path}",
110+
"i" = example_hint
111+
))
101112
}
102113
}
103114

0 commit comments

Comments
 (0)