Skip to content

miniapp-cli dev: background edits never respawn the JSContext on Windows (path separators) #3888

Description

@skthegawd

Summary

On Windows, miniapp-cli dev never respawns the background JSContext. Every edit under
src/background/ broadcasts reload (WebView only) instead of respawn-bg, so the phone keeps
running the previously installed bundle while the terminal reports activity and dist/ is correctly
rebuilt.

The failure is silent and looks like a working dev loop. It cost us about an hour of debugging a
stale bundle before we noticed the terminal line was reload src\background rather than
respawn-bg src/background/index.ts.

Cause

src/dev-server.ts decides which layer changed with forward-slash string tests:

const touchedBackground =
  filename.startsWith("src/background/") || filename.includes("/src/background/")

Windows fs.watch reports src\background\index.ts, so touchedBackground is always false and
nextType is always "reload".

The same assumption breaks the watcher's skip list:

const isUnder = (filename: string, name: string): boolean =>
  filename === name || filename.startsWith(`${name}/`) || filename.includes(`/${name}/`)

so isUnder(filename, "dist") never matches either, and build output is not filtered out of the
watcher on Windows.

Reproduction

  1. On Windows, bunx @mentra/miniapp-cli dev in a two-layer project.
  2. Install on a phone by scanning the QR.
  3. Edit anything under src/background/, e.g. add a console.log at the top of the handler.
  4. Observe the terminal prints reload src\background (note the backslash).
  5. The new log line never appears in the forwarded [MentraJS] output — the phone is still running
    the old bundle, even though dist/background/index.js has a fresh mtime and contains the change.

Suggested fix

Normalise the separator once at the watcher boundary, before any path test:

const norm = (p: string): string => p.replace(/\\/g, "/")

const isUnder = (filename: string, name: string): boolean => {
  const f = norm(filename)
  return f === name || f.startsWith(`${name}/`) || f.includes(`/${name}/`)
}

const watcher = watch(options.watchDir, {recursive: true}, (_event, rawFilename) => {
  const filename = rawFilename === null ? null : norm(rawFilename)
  if (!filename) return
  // ...unchanged
})

Verified against a local copy of @mentra/miniapp-cli@0.1.0-dev.0: after this change a background
edit prints respawn-bg src/background/index.ts and the phone picks up the new bundle without a
reinstall.

Environment

  • @mentra/miniapp-cli 0.1.0-dev.0, @mentra/miniapp 3.1.0-dev.27
  • Host: Windows 11, Bun 1.x
  • Device: Mentra Live, MentraOS app staging.20260808.*

Note

The project README we inherited advised "delete the entry in Miniapp Developer Settings and rescan to
pick up background changes". That workaround is a correct observation of this bug, and it may be
worth checking whether the docs recommend it anywhere — on macOS/Linux respawn-bg works and the
advice is unnecessary.

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