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
Summary
simple_module_hosting'ssync-js-depscommand (used by the newmake installtarget the scaffold ships) hardcodesclient_app.parent.parentas the workspace root. That heuristic matches the framework's own repo layout (<repo>/host/client_app/) but is wrong for the flat scaffoldsm newproduces (<repo>/client_app/), where there's only one parent aboveclient_app/. Result:make installfails withENOENTreading apackage.jsontwo levels above the worktree.Reproduction
After fixing #126, #127, #128:
(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:Then in
host_cli.sync_js_deps:npmis invoked from the grandparent ofclient_app/. In the framework repo that's<repo>/host/client_app/→<repo>/. In the flat scaffold that's<repo>/client_app/→<repo>/.., which has nopackage.json.Why this matters more than "just an ENOENT"
The npm-workspace shape from the new
sm newscaffold requires the rootpackage.jsonto be at<scaffold_root>/package.json(that's where\"workspaces\": [\"client_app\", \"modules/*\"]is declared). Butrepo_root_from_client_appis hardcoded to skip past it. Sosync-js-depswould 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 devwork end-to-end (verified locally) is the workspace structure itself: with a rootpackage.jsondeclaringworkspaces, vite can walk up from.venv/.../site-packages/<mod>/*.tsxand find the workspace rootnode_modules. Sosync-js-depsmay not even be load-bearing for the bundled modules. But the broken heuristic still killsmake install.Suggested fix
Replace the hardcoded walk with a proper search. Walk up from
client_app/looking for the first ancestor that has apackage.jsondeclaringworkspaces(or, failing that, the first ancestor with anypackage.json):That handles both layouts correctly:
host/client_app/→ walks up to repo root withworkspaces.sm newscaffold:client_app/→ walks up one level to scaffold root withworkspaces.Workaround
Comment out
sync-js-depsfrom the Makefile'sinstalltarget, or runmake installwithmake sync-js-deps -ito ignore the failure. The@simple-module-py/ui@0.0.8package brings the bundled modules' transitive deps anyway, sosync-js-depsfinding 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_hosting0.0.8simple_module_cli0.0.8sm newscaffold output