Skip to content

Worker path helper: resolve await helper() and if/return helper bodies (OpenCode TUI worker: const file = await target(); new Worker(file)) #10236

Description

@proggeramlug

Summary

new Worker(file) is only compiled into a native worker entry when the compile-time worker-path helper (crates/perry-hir/src/dynamic_import/worker_paths.rs) can evaluate file. It accepts string literals, new URL(x, import.meta.url), path joins, ternaries and calls to synchronous single-return helpers, and rejects everything else: async and generator helpers are not static path helpers, helper body must contain only a single return (no effects, mutation or multiple returns), and Expr::Await falls into unsupported expression (effects, mutation and opaque calls are not evaluated). The runtime then throws worker_threads Worker filename was not statically resolvable at compile time; constructing this Worker is unsupported in the compiled binary.

OpenCode v1.18.30 (tracker #10107) picks its TUI worker like this (packages/opencode/src/cli/cmd/tui.ts), with OPENCODE_WORKER_PATH supplied as a --define string by scripts/build_opencode.ts:

declare const OPENCODE_WORKER_PATH: string
async function target() {
  if (typeof OPENCODE_WORKER_PATH !== "undefined") return OPENCODE_WORKER_PATH
  const dist = new URL("./cli/tui/worker.js", import.meta.url)
  if (await Filesystem.exists(fileURLToPath(dist))) return dist
  return new URL("../tui/worker.ts", import.meta.url)
}

const file = await target()
const worker = new Worker(file, { env:  })

The full compile warns worker_threads Worker in module cli/cmd/tui.ts: Worker path helper: unsupported expression (effects, mutation and opaque calls are not evaluated) — this Worker will throw if constructed at runtime, and opencode (the TUI, the default command) dies right after startup with the runtime error above. Every other rung is fine.

Ask

Extend the helper so this shape resolves, without evaluating effects:

  1. await <expr> → resolve <expr> (an await does not change which paths are possible).
  2. A helper (sync or async) whose body is a chain of if (<cond>) return <e_i>; / const x = <pure expr>; statements ending in return <e_n>; resolves to the union of all return expressions' paths; conditions are not evaluated (an opaque call like await Filesystem.exists(...) inside a condition is fine because the value is still one of the returns). Union semantics are sound for this purpose: extra candidates only cost an extra compiled worker entry.
  3. With a --define, typeof OPENCODE_WORKER_PATH !== "undefined" is a constant; folding it is optional once (2) uses union semantics.
  4. A candidate that resolves to a file that does not exist at compile time (here ./cli/tui/worker.js, the bun-build layout) should be skipped with a warning rather than failing the whole resolution; duplicates (OPENCODE_WORKER_PATH and ../tui/worker.ts name the same file) collapse to one entry.
  5. Keep the current limits (work budget, string limit, recursion guard) and the current rejections for real effects (assignments, calls whose result is returned from an opaque callee).

Expected: opencode (TUI) constructs its worker natively; perry compile reports the worker entry in its summary instead of the warning.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions