Skip to content

sync-js-deps assumes host/client_app/ layout (parent.parent walk) — fails ENOENT on flat sm new scaffold #129

Description

@antosubash

Summary

simple_module_hosting's sync-js-deps command (used by the new make install target the scaffold ships) hardcodes client_app.parent.parent as the workspace root. That heuristic matches the framework's own repo layout (<repo>/host/client_app/) but is wrong for the flat scaffold sm new produces (<repo>/client_app/), where there's only one parent above client_app/. Result: make install fails with ENOENT reading a package.json two levels above the worktree.

Reproduction

After fixing #126, #127, #128:

$ make install
uv sync
...
Audited 74 packages in 0.07ms
cd client_app && npm install
...
make sync-js-deps
uv run python -m simple_module_hosting sync-js-deps --host-client-app=client_app
INFO: Discovered module: Hello (v0.1.0)
INFO: Discovered module: Settings (v1.0.0)
INFO: Discovered module: Dashboard (v1.0.0)
INFO: Discovered module: Auth (v1.0.0)
INFO: Discovered module: Permissions (v1.0.0)
INFO: Discovered module: Users (v1.0.0)
No module JS dependencies declared.
npm error code ENOENT
npm error syscall open
npm error path /Volumes/ext1/Sandbox/simple_module_chat/package.json
npm error errno -2
npm error enoent Could not read package.json: Error: ENOENT: no such file or directory, open '/Volumes/ext1/Sandbox/simple_module_chat/package.json'
make[1]: *** [sync-js-deps] Error 254
make: *** [install] Error 2

(The actual worktree is /Volumes/ext1/Sandbox/simple_module_chat/confident-lovelace-210ce5/; the heuristic walked up to its parent.)

Root cause

simple_module_hosting/manifest.py:

def repo_root_from_client_app(client_app_dir: Path) -> Path:
    \"\"\"Repo root is two levels above ``host/client_app/``.

    Both ``write_module_pages_manifest`` and the ``sync-js-deps`` CLI
    derive the workspace root from the host's client_app directory.
    Centralized here so the heuristic lives in exactly one place.
    \"\"\"
    return client_app_dir.resolve().parent.parent

Then in host_cli.sync_js_deps:

repo_root = repo_root_from_client_app(host_client_app)
...
result = subprocess.run(cmd, cwd=repo_root, check=False)  # cwd is wrong

npm is invoked from the grandparent of client_app/. In the framework repo that's <repo>/host/client_app/<repo>/. In the flat scaffold that's <repo>/client_app/<repo>/.., which has no package.json.

Why this matters more than "just an ENOENT"

The npm-workspace shape from the new sm new scaffold requires the root package.json to be at <scaffold_root>/package.json (that's where \"workspaces\": [\"client_app\", \"modules/*\"] is declared). But repo_root_from_client_app is hardcoded to skip past it. So sync-js-deps would fail in any scaffold that uses the flat-with-workspaces shape — even if there were JS deps to install.

Right now the fix that lets npm run dev work end-to-end (verified locally) is the workspace structure itself: with a root package.json declaring workspaces, vite can walk up from .venv/.../site-packages/<mod>/*.tsx and find the workspace root node_modules. So sync-js-deps may not even be load-bearing for the bundled modules. But the broken heuristic still kills make install.

Suggested fix

Replace the hardcoded walk with a proper search. Walk up from client_app/ looking for the first ancestor that has a package.json declaring workspaces (or, failing that, the first ancestor with any package.json):

def repo_root_from_client_app(client_app_dir: Path) -> Path:
    here = client_app_dir.resolve()
    for parent in (here.parent, *here.parents):
        pkg = parent / \"package.json\"
        if pkg.is_file():
            try:
                data = json.loads(pkg.read_text())
            except json.JSONDecodeError:
                continue
            if \"workspaces\" in data:
                return parent
    # Fallback: keep current behavior so the framework repo still works.
    return here.parent.parent

That handles both layouts correctly:

  • Framework repo: host/client_app/ → walks up to repo root with workspaces.
  • Flat sm new scaffold: client_app/ → walks up one level to scaffold root with workspaces.

Workaround

Comment out sync-js-deps from the Makefile's install target, or run make install with make sync-js-deps -i to ignore the failure. The @simple-module-py/ui@0.0.8 package brings the bundled modules' transitive deps anyway, so sync-js-deps finding nothing to install (No module JS dependencies declared.) is fine — the failure is purely in the npm-cwd, not in the dep resolution.

Related

Environment

  • simple_module_hosting 0.0.8
  • simple_module_cli 0.0.8
  • The new flat-with-workspaces sm new scaffold output

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

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions