simple-module-cli 0.0.35__tar.gz → 0.0.36__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/PKG-INFO +1 -1
  2. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/pyproject.toml +1 -1
  3. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/skills/simple-module-migrations/SKILL.md +7 -9
  4. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/host/client_app/app.tsx +11 -1
  5. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/host/migrations/env.py +6 -3
  6. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/host/migrations/script.py.mako +1 -0
  7. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/host/templates/index.html +1 -0
  8. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/module/tests/test___PACKAGE__.py.tpl +5 -2
  9. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_cli_new_regressions.py +13 -0
  10. simple_module_cli-0.0.36/tests/test_module_css_subdir_sources.py +47 -0
  11. simple_module_cli-0.0.36/tests/test_scaffolded_host_setup_wizard.py +57 -0
  12. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_scaffolding_module.py +10 -0
  13. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/.gitignore +0 -0
  14. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/LICENSE +0 -0
  15. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/README.md +0 -0
  16. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/__init__.py +0 -0
  17. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/_env.py +0 -0
  18. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/_module_build.py +0 -0
  19. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/_module_host.py +0 -0
  20. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/_templating.py +0 -0
  21. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/add_cmd.py +0 -0
  22. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/app_project.py +0 -0
  23. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/case.py +0 -0
  24. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/catalog.py +0 -0
  25. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/cli.py +0 -0
  26. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/docker_assets.py +0 -0
  27. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/git_source.py +0 -0
  28. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/module_cmd.py +0 -0
  29. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/new.py +0 -0
  30. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/package_update.py +0 -0
  31. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/pins.py +0 -0
  32. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/plugins.py +0 -0
  33. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/pypi.py +0 -0
  34. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/pyproject_edit.py +0 -0
  35. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/recipes.py +0 -0
  36. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/requirements.py +0 -0
  37. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/scaffolding.py +0 -0
  38. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/skills/README.md +0 -0
  39. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/skills/simple-module-cli/SKILL.md +0 -0
  40. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/skills/simple-module-conventions/SKILL.md +0 -0
  41. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/skills/simple-module-creating/SKILL.md +0 -0
  42. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/skills/simple-module-database/SKILL.md +0 -0
  43. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/skills/simple-module-doctor/SKILL.md +0 -0
  44. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/skills/simple-module-inertia-pages/SKILL.md +0 -0
  45. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/skills/simple-module-locales/SKILL.md +0 -0
  46. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/skills/simple-module-registries/SKILL.md +0 -0
  47. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/skills/simple-module-testing/SKILL.md +0 -0
  48. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/skills_cmd.py +0 -0
  49. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/docker/Makefile.snippet +0 -0
  50. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/docker/compose-base-postgres.yml.tpl +0 -0
  51. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/docker/compose-base-sqlite.yml.tpl +0 -0
  52. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/docker/compose-tasks-postgres.yml.tpl +0 -0
  53. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/docker/compose-tasks-sqlite.yml.tpl +0 -0
  54. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/docker/dockerignore +0 -0
  55. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/docker/host-flat.Dockerfile +0 -0
  56. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/docker/host.Dockerfile +0 -0
  57. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/host/.env.example +0 -0
  58. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/host/.gitignore +0 -0
  59. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/host/Makefile +0 -0
  60. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/host/README.md.tpl +0 -0
  61. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/host/_optional/background_tasks/Makefile.snippet +0 -0
  62. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/host/_optional/background_tasks/run_worker.py +0 -0
  63. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/host/alembic.ini +0 -0
  64. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/host/client_app/main.tsx +0 -0
  65. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/host/client_app/module-assets.ts +0 -0
  66. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/host/client_app/package.json.tpl +0 -0
  67. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/host/client_app/pages/Error.tsx +0 -0
  68. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/host/client_app/pages/Landing.tsx +0 -0
  69. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/host/client_app/pages.ts +0 -0
  70. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/host/client_app/styles.css +0 -0
  71. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/host/client_app/tsconfig.json +0 -0
  72. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/host/client_app/vite.config.ts +0 -0
  73. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/host/client_app/vite.dev-url.ts +0 -0
  74. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/host/main.py +0 -0
  75. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/host/migrations/versions/.gitkeep +0 -0
  76. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/host/pyproject.toml.tpl +0 -0
  77. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/host/routes.py +0 -0
  78. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/module/.github/workflows/ci.yml +0 -0
  79. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/module/.github/workflows/publish.yml.tpl +0 -0
  80. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/module/.gitignore +0 -0
  81. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/module/README.md.tpl +0 -0
  82. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/module/__PACKAGE__/__init__.py +0 -0
  83. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/module/__PACKAGE__/endpoints/__init__.py +0 -0
  84. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/module/__PACKAGE__/endpoints/api.py.tpl +0 -0
  85. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/module/__PACKAGE__/endpoints/views.py.tpl +0 -0
  86. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/module/__PACKAGE__/module.py.tpl +0 -0
  87. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/module/__PACKAGE__/pages/Index.tsx.tpl +0 -0
  88. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/module/__PACKAGE__/services.py.tpl +0 -0
  89. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/module/__PACKAGE__/settings.py.tpl +0 -0
  90. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/module/_optional/standalone/package.json.tpl +0 -0
  91. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/module/_optional/standalone/tsconfig.json.tpl +0 -0
  92. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/module/package.json.tpl +0 -0
  93. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/module/pyproject.toml.tpl +0 -0
  94. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/module/tsconfig.json.tpl +0 -0
  95. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/workspace/.env.example +0 -0
  96. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/workspace/.gitignore +0 -0
  97. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/workspace/Makefile +0 -0
  98. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/workspace/README.md.tpl +0 -0
  99. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/workspace/package.json.tpl +0 -0
  100. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/templates/workspace/pyproject.toml.tpl +0 -0
  101. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/update_cmd.py +0 -0
  102. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/simple_module_cli/wizard.py +0 -0
  103. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/conftest.py +0 -0
  104. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_build_packaging.py +0 -0
  105. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_case.py +0 -0
  106. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_cli_add_cmd.py +0 -0
  107. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_cli_add_spec_parse.py +0 -0
  108. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_cli_catalog.py +0 -0
  109. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_cli_docker_assets.py +0 -0
  110. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_cli_git_scan.py +0 -0
  111. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_cli_new.py +0 -0
  112. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_cli_new_dest_tolerance.py +0 -0
  113. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_cli_new_git_module.py +0 -0
  114. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_cli_new_migration_bootstrap.py +0 -0
  115. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_cli_new_scaffold_layout.py +0 -0
  116. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_cli_package_update.py +0 -0
  117. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_cli_package_update_pins.py +0 -0
  118. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_cli_pyproject_edit.py +0 -0
  119. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_cli_recipes.py +0 -0
  120. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_cli_update_sources.py +0 -0
  121. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_cli_vite_port_env.py +0 -0
  122. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_cli_wizard.py +0 -0
  123. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_cli_wizard_git.py +0 -0
  124. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_create_module_ci_skip.py +0 -0
  125. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_env_helper.py +0 -0
  126. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_module_cmd_build.py +0 -0
  127. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_module_cmd_e2e.py +0 -0
  128. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_module_cmd_verify.py +0 -0
  129. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_module_css_assets.py +0 -0
  130. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_module_css_build.py +0 -0
  131. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_module_npm_aliases.py +0 -0
  132. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_module_pages_manifest.py +0 -0
  133. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_no_framework_deps.py +0 -0
  134. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_plugin_discovery.py +0 -0
  135. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_scaffold_rollback.py +0 -0
  136. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_scaffolding_host.py +0 -0
  137. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_scaffolding_module_js.py +0 -0
  138. {simple_module_cli-0.0.35 → simple_module_cli-0.0.36}/tests/test_skills_cmd.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: simple_module_cli
3
- Version: 0.0.35
3
+ Version: 0.0.36
4
4
  Summary: Standalone scaffolder for the SimpleModule framework — `smpy new`, `smpy create-module`, plugin host.
5
5
  Project-URL: Homepage, https://github.com/antosubash/simple_module_python
6
6
  Project-URL: Repository, https://github.com/antosubash/simple_module_python
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "simple_module_cli"
3
- version = "0.0.35"
3
+ version = "0.0.36"
4
4
  description = "Standalone scaffolder for the SimpleModule framework — `smpy new`, `smpy create-module`, plugin host."
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -64,7 +64,7 @@ make migration msg="add orders module"
64
64
  make migrate
65
65
  ```
66
66
 
67
- `make migrate` runs `alembic -c host/alembic.ini upgrade heads` — note **`heads`** (plural). Branch labels create multiple independent heads, so `upgrade head` (singular) would error or under-apply.
67
+ `make migrate` runs `alembic -c host/alembic.ini upgrade heads` — note **`heads`** (plural). Histories with a branch label can end up with multiple heads, so `upgrade head` (singular) may error or under-apply.
68
68
 
69
69
  ### Branch labels — set on the FIRST revision per module
70
70
 
@@ -78,16 +78,14 @@ branch_labels = ("audit_log",) # ← add by hand on this first revision
78
78
  depends_on = None
79
79
  ```
80
80
 
81
- Why: it lets operators roll back **just one module's** schema, which is how a module is uninstalled cleanly:
81
+ Why: the label is a named target for that revision (`upgrade audit_log@head`, the module column in the Doctor). It does **not** isolate the module: autogenerate chains the revision off the current head, so history is one linear chain and `downgrade audit_log@base` walks the **entire chain beneath it, other modules included**. Never use it as a per-module rollback.
82
82
 
83
- ```bash
84
- make downgrade # back one revision; OR, to a target:
85
- uv run --project host alembic -c host/alembic.ini downgrade audit_log@base
86
- ```
83
+ To remove one module's schema:
87
84
 
88
- `audit_log@base` drops everything that module owns and leaves the others. Without the label, downgrading to a bare revision id walks linear history and rolls back unrelated modules' migrations sitting between.
85
+ - If the module's revisions are the latest in the chain, downgrade to the `down_revision` of its first revision: `uv run --project host alembic -c host/alembic.ini downgrade <that down_revision id>`.
86
+ - Otherwise write a dedicated migration that drops that module's tables.
89
87
 
90
- Subsequent revisions for the same module **don't** need a `branch_labels` — only the first; they inherit the branch.
88
+ Subsequent revisions for the same module **don't** need a `branch_labels` — only the first; they chain off it as ordinary revisions.
91
89
 
92
90
  > Exception: the repo's bootstrap `initial_schema` revision bundles several core modules (users, permissions, settings, file_storage, feature_flags, background_tasks) into one unlabeled root migration. The per-module `branch_labels` convention applies to modules added **after** that bootstrap.
93
91
 
@@ -112,7 +110,7 @@ SM010/SM011 are **not** emitted at boot or by `make doctor`. They only surface w
112
110
 
113
111
  ## Pitfalls
114
112
 
115
- - **Skipped the branch label on the first revision.** `alembic downgrade <module>@base` then fails or silently rolls back unrelated revisions. Fix by editing the migration file before applying; if already deployed, write a no-op revision that adds the label retroactively.
113
+ - **Treating `downgrade <module>@base` as a per-module rollback.** It rolls back every revision beneath the labelled one, other modules included. See the recipe under "Branch labels".
116
114
  - **Renamed a column.** Autogenerate emits `drop_column` + `add_column`, which loses data. Edit to `op.alter_column(..., new_column_name=...)` and write the matching `downgrade()`.
117
115
  - **Hand-edited operations with stubbed `downgrade()`.** Server defaults, CHECK constraints, expression indexes — Alembic can't always infer these. When you fill in `upgrade()`, fill in `downgrade()` too.
118
116
  - **Concurrent `make migrate` (`upgrade heads`) from multiple processes.** The `alembic_version` table isn't race-safe across all backends. Run upgrades from one place (a release pipeline step), not from the booting app.
@@ -1,6 +1,8 @@
1
1
  import { createInertiaApp, router } from '@inertiajs/react';
2
2
  import { configureI18n, updateI18n } from '@simple-module-py/i18n';
3
+ import { formatTitle, setTitleAppName } from '@simple-module-py/ui/lib/app-title';
3
4
  import { startSpaLinkInterception } from '@simple-module-py/ui/lib/spa-links';
5
+ import { initTheme } from '@simple-module-py/ui/lib/theme';
4
6
  import { createRoot } from 'react-dom/client';
5
7
  import { resolvePage } from './pages';
6
8
 
@@ -11,11 +13,19 @@ type I18nBlock = {
11
13
  };
12
14
 
13
15
  createInertiaApp({
16
+ title: (title) => formatTitle(title),
14
17
  resolve: async (name) => {
15
18
  return await resolvePage(name);
16
19
  },
17
20
  setup({ el, App, props }) {
18
- const initial = (props.initialPage.props as { i18n?: I18nBlock }).i18n;
21
+ const pageProps = props.initialPage.props as {
22
+ i18n?: I18nBlock;
23
+ branding?: { appName?: string | null };
24
+ };
25
+ setTitleAppName(pageProps.branding?.appName);
26
+ // Before the first render, so a dark-theme user never sees a light frame.
27
+ initTheme();
28
+ const initial = pageProps.i18n;
19
29
  configureI18n({
20
30
  locale: initial?.locale ?? 'en',
21
31
  messages: initial?.messages ?? {},
@@ -27,12 +27,15 @@ logger = logging.getLogger("alembic.env")
27
27
  config = context.config
28
28
 
29
29
  if config.config_file_name is not None:
30
- fileConfig(config.config_file_name)
30
+ # Not the default disable_existing_loggers=True: the setup wizard runs this
31
+ # in-process, and that default would silence every app logger until restart.
32
+ fileConfig(config.config_file_name, disable_existing_loggers=False)
31
33
 
32
34
  target_metadata = build_module_metadata()
33
35
  include_object = make_include_object(target_metadata)
34
- # Re-emit expression-based indexes (e.g. ``lower(email)``) that autogenerate
35
- # silently drops under SQLite. See ``make_process_revision_directives`` docstring.
36
+ # Re-emit expression-based indexes that autogenerate drops under SQLite, rewrite
37
+ # Boolean 0/1 server defaults to sa.false()/sa.true(), and warn that SQLite cannot
38
+ # verify expression indexes (GH #342) — see make_process_revision_directives.
36
39
  process_revision_directives = make_process_revision_directives(target_metadata)
37
40
 
38
41
 
@@ -8,6 +8,7 @@ Create Date: ${create_date}
8
8
  from collections.abc import Sequence
9
9
 
10
10
  import sqlalchemy as sa
11
+ import sqlmodel # noqa: F401 (autogenerate may emit sqlmodel types)
11
12
  from alembic import op
12
13
  ${imports if imports else ""}
13
14
 
@@ -9,6 +9,7 @@
9
9
  {% set brand = branding_head(request) %}
10
10
  <title data-inertia="">{{ brand.app_name }}</title>
11
11
  {% if brand.theme_color %}<meta name="theme-color" content="{{ brand.theme_color }}" />{% endif %}
12
+ {% if brand.favicon_url %}<link rel="icon" href="{{ brand.favicon_url }}" />{% endif %}
12
13
  <link rel="preconnect" href="https://fonts.googleapis.com" />
13
14
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
14
15
  <link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700;1,9..40,400&family=Outfit:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
@@ -8,6 +8,7 @@ that package is installed). No conftest.py is required.
8
8
  from __future__ import annotations
9
9
 
10
10
  from {{PACKAGE_NAME}}.module import {{MODULE_NAME}}Module
11
+ from simple_module_test import effective_route_paths
11
12
 
12
13
 
13
14
  class TestMeta:
@@ -22,6 +23,8 @@ class TestRoutes:
22
23
  async def test_app_boots_with_module(self, build_test_app):
23
24
  """Module registers cleanly into a minimal FastAPI host."""
24
25
  app = build_test_app({{MODULE_NAME}}Module)
25
- paths = {getattr(r, "path", None) for r in app.routes}
26
+ # effective_route_paths reads the OpenAPI schema: FastAPI >= 0.137
27
+ # includes routers lazily, so ``app.routes`` no longer lists them.
28
+ paths = effective_route_paths(app)
26
29
  # The placeholder GET / endpoint lives under the route_prefix.
27
- assert any(p and p.startswith("/api/{{MODULE_SLUG}}") for p in paths)
30
+ assert any(p.startswith("/api/{{MODULE_SLUG}}") for p in paths)
@@ -230,6 +230,19 @@ def test_scaffold_adopts_every_locale_catalog_the_server_sends(tmp_path: Path) -
230
230
  )
231
231
 
232
232
 
233
+ def test_sm_new_host_app_tsx_wires_theme_and_title(tmp_path: Path) -> None:
234
+ """Issue #429: the scaffolded app.tsx must init the theme and brand titles."""
235
+ from simple_module_cli.scaffolding import create_host
236
+
237
+ dest = tmp_path / "demo"
238
+ create_host(dest, name="demo-host", modules=["Dashboard", "Auth"])
239
+
240
+ app_tsx = (dest / "client_app" / "app.tsx").read_text()
241
+ assert "initTheme(" in app_tsx
242
+ assert "formatTitle" in app_tsx
243
+ assert "setTitleAppName" in app_tsx
244
+
245
+
233
246
  def test_sm_new_no_install_next_steps_include_initial_migration(tmp_path: Path) -> None:
234
247
  """Issue #135: ``host/migrations/versions/`` ships empty, so a fresh
235
248
  ``make migrate`` is a no-op. The printed next-steps must therefore
@@ -0,0 +1,47 @@
1
+ """uv's .venv/.gitignore hides subdirectories from Tailwind, so each gets its own @source (#419)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from simple_module_hosting.assets import ModuleAssets, render_modules_css
6
+
7
+
8
+ def _wheel(tmp_path, **overrides):
9
+ defaults = {
10
+ "name": "Pagebuilder",
11
+ "package_name": "pagebuilder",
12
+ "package_dir": tmp_path / "pagebuilder",
13
+ "pages_dir": None,
14
+ "theme_css": None,
15
+ "styles_css": None,
16
+ }
17
+ return ModuleAssets(**{**defaults, **overrides})
18
+
19
+
20
+ def test_each_subdirectory_with_sources_gets_its_own_line(tmp_path):
21
+ components = tmp_path / "pagebuilder" / "components"
22
+ (components / "widgets" / "quote").mkdir(parents=True)
23
+ (components / "Root.tsx").write_text("")
24
+ (components / "widgets" / "Gallery.tsx").write_text("")
25
+ (components / "widgets" / "quote" / "Quote.tsx").write_text("")
26
+ (components / "empty").mkdir()
27
+ (components / "__pycache__").mkdir()
28
+ (components / "__pycache__" / "x.ts").write_text("")
29
+
30
+ css = render_modules_css(
31
+ [_wheel(tmp_path, components_dir=components)], in_repo=lambda _p: False
32
+ )
33
+
34
+ lines = [line for line in css.splitlines() if line.startswith("@source")]
35
+ assert lines == [
36
+ f'@source "{components.as_posix()}/**/*.{{ts,tsx}}";',
37
+ f'@source "{(components / "widgets").as_posix()}/**/*.{{ts,tsx}}";',
38
+ f'@source "{(components / "widgets" / "quote").as_posix()}/**/*.{{ts,tsx}}";',
39
+ ]
40
+
41
+
42
+ def test_in_repo_module_subdirectories_get_nothing(tmp_path):
43
+ components = tmp_path / "local" / "components"
44
+ (components / "widgets").mkdir(parents=True)
45
+ (components / "widgets" / "A.tsx").write_text("")
46
+ css = render_modules_css([_wheel(tmp_path, components_dir=components)], in_repo=lambda _p: True)
47
+ assert "@source" not in css
@@ -0,0 +1,57 @@
1
+ """A freshly scaffolded host gets a working /setup with no files of its own.
2
+
3
+ GH #351: ``SetupMiddleware`` redirected every request to ``/setup`` while the
4
+ route and page lived only in the framework repo's unpublished host, so a host
5
+ made by ``smpy create-host`` answered ``/`` → ``/setup`` → 404. The wizard now
6
+ ships with ``simple_module_hosting``: ``create_app`` mounts the route and
7
+ ``gen-pages`` registers the page, so the scaffold must stay free of both.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import json
13
+ import re
14
+
15
+ import pytest
16
+ from simple_module_hosting.manifest import write_module_pages_manifest
17
+ from simple_module_hosting.setup_wizard import PAGES_NAME, pages_dir
18
+
19
+ pytestmark = pytest.mark.anyio
20
+
21
+
22
+ async def test_scaffold_carries_no_setup_code_and_resolves_the_wizard(tmp_path) -> None:
23
+ from simple_module_cli.scaffolding import create_host
24
+
25
+ dest = tmp_path / "demo"
26
+ create_host(dest, name="demo-host", modules=[])
27
+ client_app = dest / "client_app"
28
+
29
+ # Nothing host-side: no route module, no page.
30
+ assert not (dest / "routes_setup.py").exists()
31
+ assert not list((client_app / "pages").rglob("Setup*"))
32
+ assert "setup" not in (dest / "main.py").read_text(encoding="utf-8").replace(
33
+ "setup_logging", ""
34
+ )
35
+
36
+ # What `smpy gen-pages` writes for this host, with no modules installed.
37
+ write_module_pages_manifest([], client_app, repo_root=dest)
38
+
39
+ manifest = json.loads((client_app / "modules.manifest.json").read_text(encoding="utf-8"))
40
+ assert manifest[PAGES_NAME] == pages_dir().as_posix()
41
+ assert (pages_dir() / "Wizard.tsx").is_file()
42
+
43
+ generated = (client_app / "modules.generated.ts").read_text(encoding="utf-8")
44
+ assert f'"{PAGES_NAME}": import.meta.glob' in generated
45
+
46
+ # The scaffold's resolver keys a module glob entry as `<name>/<path under pages/>`,
47
+ # which is how `inertia.render("Setup/Wizard")` finds the page.
48
+ pages_ts = (client_app / "pages.ts").read_text(encoding="utf-8")
49
+ assert "pages[`${moduleName}/${match[1]}`]" in pages_ts
50
+ match = re.search(r"/pages/(.+)\.tsx$", (pages_dir() / "Wizard.tsx").as_posix())
51
+ assert match and f"{PAGES_NAME}/{match.group(1)}" == "Setup/Wizard"
52
+
53
+ # Tailwind must scan the wheel's wizard, and Vite must be allowed to serve it.
54
+ css = (client_app / "modules.generated.css").read_text(encoding="utf-8")
55
+ assert f'@source "{pages_dir().as_posix()}/**/*.{{ts,tsx}}";' in css
56
+ assets = json.loads((client_app / "modules.assets.json").read_text(encoding="utf-8"))
57
+ assert assets[PAGES_NAME]["pages"] == pages_dir().as_posix()
@@ -30,6 +30,16 @@ class TestCreateModule:
30
30
  # pytest try to register `tests.conftest` as a plugin twice.
31
31
  assert not (dest / "tests" / "__init__.py").exists()
32
32
 
33
+ async def test_generated_test_uses_effective_route_paths(self, tmp_path):
34
+ """#431: app.routes hides lazily-included routers on FastAPI >= 0.137."""
35
+ from simple_module_cli.scaffolding import create_module
36
+
37
+ dest = tmp_path / "simple-module-my-feature"
38
+ create_module(dest, name="MyFeature")
39
+ text = (dest / "tests" / "test_my_feature.py").read_text()
40
+ assert "effective_route_paths(app)" in text
41
+ assert "app.routes" not in text.replace("``app.routes``", "")
42
+
33
43
  async def test_test_file_basename_is_unique_per_module(self, tmp_path):
34
44
  """Two modules in one workspace must not ship same-named test files.
35
45