Skip to content

sm new sample module's [tool.hatch.build.targets.wheel.force-include] requires hello/static/dist/ — uv sync fails because that dir is gitignored #127

Description

@antosubash

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.8
  • hatchling (current latest)

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