Summary
After fixing #126, uv syncstill fails on a fresh sm new scaffold because the sample module's pyproject.toml force-includes hello/static/dist/ into the wheel — but that directory doesn't exist on a fresh scaffold (it's the build output of vite, gitignored, and only created later by make build).
uv sync builds workspace members as part of resolution, the hatch builder hits the missing force-include path, and the whole sync aborts.
Reproduction
$ uvx --from simple_module_cli sm new my-app --db sqlite --preset standard -y --no-install
$ cd my-app && cp .env.example .env
# (after fixing #126 — the framework version pins in modules/hello/pyproject.toml)
$ make install
...
File ".../hatchling/builders/plugin/interface.py", line 239, in recurse_forced_files
raise FileNotFoundError(msg)
FileNotFoundError: Forced include not found:
/tmp/my-app/modules/hello/hello/static/dist
hint: This usually indicates a problem with the package or the build environment.
help: `simple-module-hello` was included because `simple-module-chat`
(v0.1.0) depends on `simple-module-hello`
make: *** [install] Error 1
Root cause
modules/hello/pyproject.toml (generated):
# Ship the built frontend bundle + per-module JS dep manifest inside the wheel.# static/dist/ is gitignored, so force-include picks up the working-tree build# during `uv build` — run your bundler (vite/esbuild) first. package.json lives# at the module root so npm workspaces see it; copying it into <pkg>/ lets the# host discover JS deps via importlib.resources after a pip install.
[tool.hatch.build.targets.wheel.force-include]
"hello/static/dist" = "hello/static/dist""package.json" = "hello/package.json"
The comment is correct — static/dist/ is intentionally a build artifact only present after vite build — but force-include is not optional: hatchling raises FileNotFoundError if the source path doesn't exist when the wheel is built. And uv sync builds workspace members eagerly, before the user has a chance to run make build.
The chicken-and-egg: you can't make install (which runs uv sync first) without static/dist/, but static/dist/ is created by make build which depends on a successful npm install which is part of make install.
Suggested fix
Several options, in roughly increasing invasiveness:
A. Don't force-include static/dist/ from the sample module template
The comment in the generated pyproject says this is for publishing the wheel with bundled assets. That's a release-time concern, not a dev-time concern. The cleanest fix is for sm new to leave force-include out of the sample, and add a separate hatch build target (or doc note) for users who want to bundle assets when publishing:
# Production build: include built frontend assets in the wheel.# Run `vite build` from the workspace root before `uv build`.# [tool.hatch.build.targets.wheel.force-include]# "hello/static/dist" = "hello/static/dist"# "package.json" = "hello/package.json"
…with the lines commented out by default. Users who publish modules can uncomment and ensure vite build ran first.
B. Make the include conditional on the dir existing
Hatch supports only-include patterns that match-or-skip. Or use [tool.hatch.build.targets.wheel] sources with a glob. Less obvious-to-the-reader than (A) but doesn't break the published-wheel use case.
C. Have sm new create an empty static/dist/.gitkeep
Quickest fix to unblock — adds one file to the scaffold. Still leaves the trap that any user wiping static/dist/ (e.g. as part of a clean) will break their next uv sync.
D. Order the Makefile so vite build runs before uv sync
Inverts the dep graph in make install. Works, but means npm install has to run first, which requires hatching out the uv sync step.
I'd pick (A): the sample module is for demoing module dev, and the publish-time-bundle-the-assets pattern is a separate (advanced) workflow that should live in docs, not in every fresh scaffold.
Workaround
mkdir -p modules/hello/hello/static/dist && touch modules/hello/hello/static/dist/.gitkeep
Related
Environment
simple_module_cli 0.0.8hatchling (current latest)
Summary
After fixing #126,
uv syncstill fails on a freshsm newscaffold because the sample module'spyproject.tomlforce-includeshello/static/dist/into the wheel — but that directory doesn't exist on a fresh scaffold (it's the build output of vite, gitignored, and only created later bymake build).uv syncbuilds workspace members as part of resolution, the hatch builder hits the missingforce-includepath, and the whole sync aborts.Reproduction
Root cause
modules/hello/pyproject.toml(generated):The comment is correct —
static/dist/is intentionally a build artifact only present aftervite build— butforce-includeis not optional: hatchling raisesFileNotFoundErrorif the source path doesn't exist when the wheel is built. Anduv syncbuilds workspace members eagerly, before the user has a chance to runmake build.The chicken-and-egg: you can't
make install(which runsuv syncfirst) withoutstatic/dist/, butstatic/dist/is created bymake buildwhich depends on a successfulnpm installwhich is part ofmake install.Suggested fix
Several options, in roughly increasing invasiveness:
A. Don't force-include
static/dist/from the sample module templateThe comment in the generated pyproject says this is for publishing the wheel with bundled assets. That's a release-time concern, not a dev-time concern. The cleanest fix is for
sm newto leaveforce-includeout of the sample, and add a separate hatch build target (or doc note) for users who want to bundle assets when publishing:…with the lines commented out by default. Users who publish modules can uncomment and ensure
vite buildran first.B. Make the include conditional on the dir existing
Hatch supports
only-includepatterns that match-or-skip. Or use[tool.hatch.build.targets.wheel] sourceswith a glob. Less obvious-to-the-reader than (A) but doesn't break the published-wheel use case.C. Have
sm newcreate an emptystatic/dist/.gitkeepQuickest fix to unblock — adds one file to the scaffold. Still leaves the trap that any user wiping
static/dist/(e.g. as part of a clean) will break their nextuv sync.D. Order the Makefile so
vite buildruns beforeuv syncInverts the dep graph in
make install. Works, but meansnpm installhas to run first, which requires hatching out theuv syncstep.I'd pick (A): the sample module is for demoing module dev, and the publish-time-bundle-the-assets pattern is a separate (advanced) workflow that should live in docs, not in every fresh scaffold.
Workaround
mkdir -p modules/hello/hello/static/dist && touch modules/hello/hello/static/dist/.gitkeepRelated
Environment
simple_module_cli0.0.8hatchling(current latest)