simple-module-cli 0.0.37__tar.gz → 0.0.39__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.
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/.gitignore +1 -2
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/PKG-INFO +3 -3
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/pyproject.toml +3 -3
- simple_module_cli-0.0.39/simple_module_cli/_add_install.py +59 -0
- simple_module_cli-0.0.39/simple_module_cli/_add_sources.py +83 -0
- simple_module_cli-0.0.39/simple_module_cli/_app_project_manifests.py +94 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/add_cmd.py +7 -120
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/app_project.py +8 -89
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/case.py +26 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/catalog.py +3 -7
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/cli.py +7 -0
- simple_module_cli-0.0.39/simple_module_cli/docker_cmd.py +158 -0
- simple_module_cli-0.0.39/simple_module_cli/framework_repo.py +127 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/openapi_cmd.py +57 -33
- simple_module_cli-0.0.39/simple_module_cli/openapi_types.py +205 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/scaffolding.py +26 -1
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/skills/simple-module-conventions/SKILL.md +31 -6
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/skills/simple-module-creating/SKILL.md +30 -12
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/skills/simple-module-doctor/SKILL.md +4 -2
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/skills/simple-module-inertia-pages/SKILL.md +16 -7
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/skills/simple-module-testing/SKILL.md +2 -2
- simple_module_cli-0.0.39/simple_module_cli/templates/docker/compose-worker.yml.tpl +12 -0
- simple_module_cli-0.0.39/simple_module_cli/templates/docker/worker.Dockerfile.tpl +27 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/host/client_app/app.tsx +14 -2
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/host/templates/index.html +1 -1
- simple_module_cli-0.0.39/simple_module_cli/templates/module/__PACKAGE__/constants.py.tpl +36 -0
- simple_module_cli-0.0.39/simple_module_cli/templates/module/__PACKAGE__/contracts/__init__.py.tpl +13 -0
- simple_module_cli-0.0.39/simple_module_cli/templates/module/__PACKAGE__/contracts/schemas.py.tpl +46 -0
- simple_module_cli-0.0.39/simple_module_cli/templates/module/__PACKAGE__/deps.py.tpl +15 -0
- simple_module_cli-0.0.39/simple_module_cli/templates/module/__PACKAGE__/endpoints/api.py.tpl +91 -0
- simple_module_cli-0.0.39/simple_module_cli/templates/module/__PACKAGE__/endpoints/views.py.tpl +95 -0
- simple_module_cli-0.0.39/simple_module_cli/templates/module/__PACKAGE__/locales/en.json.tpl +27 -0
- simple_module_cli-0.0.39/simple_module_cli/templates/module/__PACKAGE__/models.py.tpl +26 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/module/__PACKAGE__/module.py.tpl +54 -9
- simple_module_cli-0.0.39/simple_module_cli/templates/module/__PACKAGE__/pages/Browse.tsx.tpl +57 -0
- simple_module_cli-0.0.39/simple_module_cli/templates/module/__PACKAGE__/pages/Create.tsx.tpl +43 -0
- simple_module_cli-0.0.39/simple_module_cli/templates/module/__PACKAGE__/pages/Edit.tsx.tpl +46 -0
- simple_module_cli-0.0.39/simple_module_cli/templates/module/__PACKAGE__/pages/api.generated.ts.tpl +266 -0
- simple_module_cli-0.0.39/simple_module_cli/templates/module/__PACKAGE__/pages/api.ts.tpl +10 -0
- simple_module_cli-0.0.39/simple_module_cli/templates/module/__PACKAGE__/py.typed +0 -0
- simple_module_cli-0.0.39/simple_module_cli/templates/module/__PACKAGE__/service.py.tpl +66 -0
- simple_module_cli-0.0.39/simple_module_cli/templates/module/__PACKAGE__/settings.py.tpl +17 -0
- simple_module_cli-0.0.39/simple_module_cli/templates/module/_optional/framework_repo/LICENSE +21 -0
- simple_module_cli-0.0.39/simple_module_cli/templates/module/_optional/framework_repo/README.md.tpl +34 -0
- simple_module_cli-0.0.39/simple_module_cli/templates/module/_optional/framework_repo/__PACKAGE__/pages/Browse.tsx.tpl +63 -0
- simple_module_cli-0.0.39/simple_module_cli/templates/module/_optional/framework_repo/__PACKAGE__/pages/Create.tsx.tpl +45 -0
- simple_module_cli-0.0.39/simple_module_cli/templates/module/_optional/framework_repo/__PACKAGE__/pages/Edit.tsx.tpl +48 -0
- simple_module_cli-0.0.39/simple_module_cli/templates/module/_optional/framework_repo/pyproject.toml.tpl +44 -0
- simple_module_cli-0.0.39/simple_module_cli/templates/module/tests/test___PACKAGE__.py.tpl +126 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/conftest.py +15 -0
- simple_module_cli-0.0.39/tests/test_cli_asset_prerequisites.py +19 -0
- simple_module_cli-0.0.39/tests/test_cli_catalog_depends_on.py +49 -0
- simple_module_cli-0.0.39/tests/test_cli_docker_workers.py +162 -0
- simple_module_cli-0.0.39/tests/test_cli_gen_types_scaffold.py +65 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_cli_new_regressions.py +3 -1
- simple_module_cli-0.0.39/tests/test_cli_openapi_gen_types.py +214 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_cli_wizard.py +4 -4
- simple_module_cli-0.0.39/tests/test_framework_repo_wiring.py +125 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_module_css_build.py +11 -8
- simple_module_cli-0.0.39/tests/test_scaffold_app_tsx_spa_links.py +9 -0
- simple_module_cli-0.0.39/tests/test_scaffolding_framework_repo.py +140 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_scaffolding_module.py +1 -1
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_scaffolding_module_js.py +7 -4
- simple_module_cli-0.0.39/tests/test_template_requires_framework.py +24 -0
- simple_module_cli-0.0.37/simple_module_cli/templates/module/__PACKAGE__/endpoints/api.py.tpl +0 -11
- simple_module_cli-0.0.37/simple_module_cli/templates/module/__PACKAGE__/endpoints/views.py.tpl +0 -16
- simple_module_cli-0.0.37/simple_module_cli/templates/module/__PACKAGE__/pages/Index.tsx.tpl +0 -13
- simple_module_cli-0.0.37/simple_module_cli/templates/module/__PACKAGE__/settings.py.tpl +0 -14
- simple_module_cli-0.0.37/simple_module_cli/templates/module/tests/test___PACKAGE__.py.tpl +0 -30
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/LICENSE +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/README.md +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/__init__.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/_env.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/_local_env.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/_module_build.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/_module_host.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/_templating.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/docker_assets.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/git_source.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/module_cmd.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/new.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/package_update.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/pins.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/plugins.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/pypi.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/pyproject_edit.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/recipes.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/requirements.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/skills/README.md +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/skills/simple-module-cli/SKILL.md +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/skills/simple-module-database/SKILL.md +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/skills/simple-module-locales/SKILL.md +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/skills/simple-module-migrations/SKILL.md +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/skills/simple-module-registries/SKILL.md +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/skills_cmd.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/docker/Makefile.snippet +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/docker/compose-base-postgres.yml.tpl +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/docker/compose-base-sqlite.yml.tpl +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/docker/compose-tasks-postgres.yml.tpl +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/docker/compose-tasks-sqlite.yml.tpl +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/docker/dockerignore +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/docker/host-flat.Dockerfile +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/docker/host.Dockerfile +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/host/.env.example +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/host/.gitignore +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/host/Makefile +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/host/README.md.tpl +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/host/_optional/background_tasks/Makefile.snippet +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/host/_optional/background_tasks/run_worker.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/host/alembic.ini +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/host/client_app/main.tsx +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/host/client_app/module-assets.ts +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/host/client_app/package.json.tpl +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/host/client_app/pages/Error.tsx +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/host/client_app/pages/Landing.tsx +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/host/client_app/pages.ts +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/host/client_app/styles.css +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/host/client_app/tsconfig.json +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/host/client_app/vite.config.ts +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/host/client_app/vite.dev-url.ts +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/host/main.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/host/migrations/env.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/host/migrations/script.py.mako +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/host/migrations/versions/.gitkeep +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/host/pyproject.toml.tpl +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/host/routes.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/module/.github/workflows/ci.yml +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/module/.github/workflows/publish.yml.tpl +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/module/.gitignore +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/module/README.md.tpl +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/module/__PACKAGE__/__init__.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/module/__PACKAGE__/endpoints/__init__.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/module/__PACKAGE__/services.py.tpl +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/module/_optional/standalone/package.json.tpl +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/module/_optional/standalone/tsconfig.json.tpl +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/module/package.json.tpl +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/module/pyproject.toml.tpl +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/module/tsconfig.json.tpl +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/workspace/.env.example +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/workspace/.gitignore +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/workspace/Makefile +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/workspace/README.md.tpl +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/workspace/package.json.tpl +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/templates/workspace/pyproject.toml.tpl +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/update_cmd.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/uv_workspace.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/simple_module_cli/wizard.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_build_packaging.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_case.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_cli_add_cmd.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_cli_add_spec_parse.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_cli_add_workspace_member.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_cli_catalog.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_cli_docker_assets.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_cli_git_scan.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_cli_new.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_cli_new_dest_tolerance.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_cli_new_git_module.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_cli_new_migration_bootstrap.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_cli_new_scaffold_layout.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_cli_openapi_export.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_cli_package_update.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_cli_package_update_pins.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_cli_pyproject_edit.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_cli_recipes.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_cli_update_sources.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_cli_vite_port_env.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_cli_wizard_git.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_create_module_ci_skip.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_env_helper.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_module_cmd_build.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_module_cmd_e2e.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_module_cmd_verify.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_module_css_assets.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_module_css_subdir_sources.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_module_npm_aliases.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_module_pages_manifest.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_no_framework_deps.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_plugin_discovery.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_scaffold_rollback.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_scaffolded_host_setup_wizard.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/tests/test_scaffolding_host.py +0 -0
- {simple_module_cli-0.0.37 → simple_module_cli-0.0.39}/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.
|
|
3
|
+
Version: 0.0.39
|
|
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
|
|
@@ -19,8 +19,8 @@ Classifier: Topic :: Software Development :: Code Generators
|
|
|
19
19
|
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
20
20
|
Classifier: Typing :: Typed
|
|
21
21
|
Requires-Python: >=3.12
|
|
22
|
-
Requires-Dist: tomlkit
|
|
23
|
-
Requires-Dist: typer
|
|
22
|
+
Requires-Dist: tomlkit<0.16,>=0.13
|
|
23
|
+
Requires-Dist: typer<0.28,>=0.12
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
|
|
26
26
|
# simple_module_cli
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "simple_module_cli"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.39"
|
|
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"
|
|
@@ -20,8 +20,8 @@ classifiers = [
|
|
|
20
20
|
"Typing :: Typed",
|
|
21
21
|
]
|
|
22
22
|
dependencies = [
|
|
23
|
-
"typer>=0.12",
|
|
24
|
-
"tomlkit>=0.13",
|
|
23
|
+
"typer>=0.12,<0.28",
|
|
24
|
+
"tomlkit>=0.13,<0.16",
|
|
25
25
|
]
|
|
26
26
|
|
|
27
27
|
[project.scripts]
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Post-install steps and shared helpers for ``smpy add``."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import subprocess
|
|
6
|
+
from collections.abc import Callable
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
|
|
11
|
+
ExecRunner = Callable[[list[str], Path], int]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
_ENTRYPOINT_CHECK = (
|
|
15
|
+
"import sys\n"
|
|
16
|
+
"from importlib.metadata import distributions\n"
|
|
17
|
+
"target = sys.argv[1].replace('-', '_').lower()\n"
|
|
18
|
+
"for dist in distributions():\n"
|
|
19
|
+
" if any(ep.group == 'simple_module' for ep in dist.entry_points):\n"
|
|
20
|
+
" name = (dist.metadata['Name'] or '').replace('-', '_').lower()\n"
|
|
21
|
+
" if name == target:\n"
|
|
22
|
+
" sys.exit(0)\n"
|
|
23
|
+
"sys.exit(1)\n"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _default_exec_runner(cmd: list[str], cwd: Path) -> int:
|
|
28
|
+
return subprocess.run(cmd, cwd=cwd, check=False).returncode
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _fail(message: str) -> typer.Exit:
|
|
32
|
+
typer.echo(f"ERROR: {message}", err=True)
|
|
33
|
+
return typer.Exit(code=1)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def post_install(
|
|
37
|
+
project_dir: Path,
|
|
38
|
+
dists: list[str],
|
|
39
|
+
models_dists: list[str],
|
|
40
|
+
exec_runner: ExecRunner,
|
|
41
|
+
) -> None:
|
|
42
|
+
"""uv sync → gen-pages → sync-js-deps → entry-point check → reminders."""
|
|
43
|
+
if exec_runner(["uv", "sync"], project_dir) != 0:
|
|
44
|
+
raise _fail("`uv sync` failed — pyproject was written; fix and re-run `uv sync`.")
|
|
45
|
+
if (project_dir / "client_app").is_dir():
|
|
46
|
+
exec_runner(["uv", "run", "smpy", "host", "gen-pages"], project_dir)
|
|
47
|
+
exec_runner(["uv", "run", "smpy", "host", "sync-js-deps"], project_dir)
|
|
48
|
+
for dist in dists:
|
|
49
|
+
code = exec_runner(["uv", "run", "python", "-c", _ENTRYPOINT_CHECK, dist], project_dir)
|
|
50
|
+
if code != 0:
|
|
51
|
+
raise _fail(
|
|
52
|
+
f"{dist} installed but exposes no [project.entry-points.simple_module] "
|
|
53
|
+
"entry point — it is not a SimpleModule module."
|
|
54
|
+
)
|
|
55
|
+
for dist in models_dists:
|
|
56
|
+
typer.echo(
|
|
57
|
+
f"{dist} ships database models — generate and apply the migration:\n"
|
|
58
|
+
f' make migration msg="add {dist}"\n make migrate'
|
|
59
|
+
)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""PyPI and local-path sources for ``smpy add``."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
|
|
10
|
+
from simple_module_cli._add_install import ExecRunner, _fail, post_install
|
|
11
|
+
from simple_module_cli.git_source import derive_range, scan_modules
|
|
12
|
+
from simple_module_cli.pyproject_edit import (
|
|
13
|
+
save_pyproject,
|
|
14
|
+
write_dependency,
|
|
15
|
+
write_path_source,
|
|
16
|
+
write_workspace_source,
|
|
17
|
+
)
|
|
18
|
+
from simple_module_cli.uv_workspace import is_workspace_member
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _add_pypi(doc, pyproject: Path, raw: str, no_sync: bool, exec_runner: ExecRunner) -> list[str]:
|
|
22
|
+
name = raw
|
|
23
|
+
for i, ch in enumerate(raw):
|
|
24
|
+
if ch in "<>=!~[; ":
|
|
25
|
+
name = raw[:i]
|
|
26
|
+
break
|
|
27
|
+
constraint = raw[len(name) :]
|
|
28
|
+
write_dependency(doc, name, constraint)
|
|
29
|
+
save_pyproject(pyproject, doc)
|
|
30
|
+
typer.echo(f"Added {name} (PyPI).")
|
|
31
|
+
if not no_sync:
|
|
32
|
+
post_install(pyproject.parent, [name], [], exec_runner)
|
|
33
|
+
return [name]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _resolve_local_path(project_dir: Path, spec_path: Path) -> tuple[Path, str]:
|
|
37
|
+
"""Resolve a local-path spec to ``(directory, text written to pyproject)``.
|
|
38
|
+
|
|
39
|
+
A relative path is resolved against the ``--path`` project (the pyproject
|
|
40
|
+
being edited, which is what ``[tool.uv.sources]`` paths are relative to).
|
|
41
|
+
If nothing exists there but the path exists relative to the cwd, the cwd
|
|
42
|
+
one is used and the written path is re-expressed relative to the project.
|
|
43
|
+
"""
|
|
44
|
+
if spec_path.is_absolute():
|
|
45
|
+
return spec_path, str(spec_path)
|
|
46
|
+
root = project_dir / spec_path
|
|
47
|
+
if not root.exists() and (Path.cwd() / spec_path).exists():
|
|
48
|
+
root = Path.cwd() / spec_path
|
|
49
|
+
return root, os.path.relpath(root.resolve(), project_dir.resolve())
|
|
50
|
+
return root, str(spec_path)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _add_path(
|
|
54
|
+
doc, pyproject: Path, spec_path: Path, no_sync: bool, exec_runner: ExecRunner
|
|
55
|
+
) -> list[str]:
|
|
56
|
+
root, written = _resolve_local_path(pyproject.parent, spec_path)
|
|
57
|
+
found = [m for m in scan_modules(root) if m.subdirectory is None]
|
|
58
|
+
if not found:
|
|
59
|
+
raise _fail(
|
|
60
|
+
f"{root} has no [project.entry-points.simple_module] entry point at its root "
|
|
61
|
+
f"(relative paths resolve against the --path project, {pyproject.parent}, "
|
|
62
|
+
"falling back to the current directory)"
|
|
63
|
+
)
|
|
64
|
+
mod = found[0]
|
|
65
|
+
if is_workspace_member(pyproject.parent, root):
|
|
66
|
+
# uv refuses a path source for a workspace member; the scaffold's own
|
|
67
|
+
# sample module is wired the same way (unpinned + workspace = true).
|
|
68
|
+
write_dependency(doc, mod.dist_name, "")
|
|
69
|
+
write_workspace_source(doc, mod.dist_name)
|
|
70
|
+
typer.echo(f"Added {mod.dist_name} (uv workspace member {written}).")
|
|
71
|
+
else:
|
|
72
|
+
write_dependency(doc, mod.dist_name, derive_range(mod.version))
|
|
73
|
+
write_path_source(doc, mod.dist_name, path=written)
|
|
74
|
+
typer.echo(f"Added {mod.dist_name} (editable path {written}).")
|
|
75
|
+
save_pyproject(pyproject, doc)
|
|
76
|
+
if not no_sync:
|
|
77
|
+
post_install(
|
|
78
|
+
pyproject.parent,
|
|
79
|
+
[mod.dist_name],
|
|
80
|
+
[mod.dist_name] if mod.ships_models else [],
|
|
81
|
+
exec_runner,
|
|
82
|
+
)
|
|
83
|
+
return [mod.dist_name]
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Dependency pins, ``.env`` DB URLs and ``pyproject.toml`` rewriting for ``smpy new``."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Sequence
|
|
6
|
+
|
|
7
|
+
from simple_module_cli.scaffolding import resolve_framework_version
|
|
8
|
+
|
|
9
|
+
_FRAMEWORK_VERSION = resolve_framework_version()
|
|
10
|
+
|
|
11
|
+
# Pin ``simple_module_cli`` as a dev dep so ``uv run smpy`` resolves to the
|
|
12
|
+
# project venv. The global ``uv tool`` install runs in its own isolated venv
|
|
13
|
+
# that can't see the project's plugin entry points (issue #134). The lint /
|
|
14
|
+
# test tooling (ruff, ty, pytest-*) backs the generated `make lint`/`make
|
|
15
|
+
# test` targets so a fresh app can run its own quality gates.
|
|
16
|
+
_APP_PY_DEV_DEPS = [
|
|
17
|
+
f"simple_module_test=={_FRAMEWORK_VERSION}",
|
|
18
|
+
f"simple_module_cli=={_FRAMEWORK_VERSION}",
|
|
19
|
+
"pytest>=8.0",
|
|
20
|
+
"pytest-asyncio>=0.24",
|
|
21
|
+
"pytest-playwright>=0.7.2",
|
|
22
|
+
# Floor: unpinned, uv backtracks playwright to 1.18 (GH #445).
|
|
23
|
+
"playwright>=1.45",
|
|
24
|
+
"ruff>=0.8",
|
|
25
|
+
"ty>=0.0.29",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
_APP_NPM_DEPS = {
|
|
29
|
+
"@simple-module-py/ui": _FRAMEWORK_VERSION,
|
|
30
|
+
"@simple-module-py/i18n": _FRAMEWORK_VERSION,
|
|
31
|
+
"react": "^19.0.0",
|
|
32
|
+
"react-dom": "^19.0.0",
|
|
33
|
+
"@inertiajs/react": "^3.7.0",
|
|
34
|
+
}
|
|
35
|
+
# Flat mode has no npm workspaces, so `client_app/package.json` is never
|
|
36
|
+
# installed — the root package.json seeded from these two dicts is the only
|
|
37
|
+
# manifest npm reads. It must therefore cover everything client_app needs to
|
|
38
|
+
# build, not just the framework pins: `vite.config.ts` imports
|
|
39
|
+
# `@tailwindcss/vite`, and `tsc` needs the @types packages. Keep this in step
|
|
40
|
+
# with templates/host/client_app/package.json.tpl.
|
|
41
|
+
_APP_NPM_DEV_DEPS = {
|
|
42
|
+
"@simple-module-py/tsconfig": _FRAMEWORK_VERSION,
|
|
43
|
+
"@tailwindcss/vite": "^4.0.0",
|
|
44
|
+
"@types/node": "^26.5.0",
|
|
45
|
+
"@types/react": "^19.0.0",
|
|
46
|
+
"@types/react-dom": "^19.0.0",
|
|
47
|
+
"@vitejs/plugin-react": "^6.1.1",
|
|
48
|
+
"tailwindcss": "^4.0.0",
|
|
49
|
+
"typescript": "^7.0.2",
|
|
50
|
+
"vite": "^8.2.2",
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _db_url(db: str, db_name: str, *, flat: bool, password: str = "<POSTGRES_PASSWORD>") -> str:
|
|
55
|
+
if db == "postgres":
|
|
56
|
+
return f"postgresql+asyncpg://postgres:{password}@localhost:5432/{db_name}"
|
|
57
|
+
# Workspace mode keeps the SQLite file next to host/'s alembic.ini so
|
|
58
|
+
# `cd host && uvicorn` and `cd host && alembic` resolve the same path.
|
|
59
|
+
return "sqlite+aiosqlite:///./app.db" if flat else "sqlite+aiosqlite:///./host/app.db"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _rewrite_pyproject(
|
|
63
|
+
text: str,
|
|
64
|
+
deps: list[str],
|
|
65
|
+
dev_deps: list[str],
|
|
66
|
+
*,
|
|
67
|
+
sources: Sequence[str] = (),
|
|
68
|
+
project_name: str | None = None,
|
|
69
|
+
) -> str:
|
|
70
|
+
"""Replace deps in a host ``pyproject.toml`` and pin workspace sources.
|
|
71
|
+
|
|
72
|
+
``sources`` lists ``simple_module_*`` packages that should resolve from
|
|
73
|
+
the uv workspace (``modules/*``) instead of PyPI. Emits a
|
|
74
|
+
``[tool.uv.sources]`` block per entry. Empty in flat mode.
|
|
75
|
+
|
|
76
|
+
``project_name`` overrides ``[project].name`` — set in workspace mode
|
|
77
|
+
so the host's package name differs from the workspace root's.
|
|
78
|
+
"""
|
|
79
|
+
import tomlkit
|
|
80
|
+
|
|
81
|
+
doc = tomlkit.parse(text)
|
|
82
|
+
project = doc.setdefault("project", tomlkit.table())
|
|
83
|
+
if project_name is not None:
|
|
84
|
+
project["name"] = project_name
|
|
85
|
+
project["dependencies"] = list(deps)
|
|
86
|
+
groups = doc.setdefault("dependency-groups", tomlkit.table())
|
|
87
|
+
groups["dev"] = list(dev_deps)
|
|
88
|
+
if sources:
|
|
89
|
+
tool = doc.setdefault("tool", tomlkit.table())
|
|
90
|
+
uv_table = tool.setdefault("uv", tomlkit.table())
|
|
91
|
+
uv_sources = uv_table.setdefault("sources", tomlkit.table())
|
|
92
|
+
for src in sources:
|
|
93
|
+
uv_sources[src] = {"workspace": True}
|
|
94
|
+
return tomlkit.dumps(doc)
|
|
@@ -11,15 +11,19 @@ sync-js-deps`` → entry-point verification, unless ``--no-sync``.
|
|
|
11
11
|
|
|
12
12
|
from __future__ import annotations
|
|
13
13
|
|
|
14
|
-
import os
|
|
15
|
-
import subprocess
|
|
16
14
|
import tempfile
|
|
17
|
-
from collections.abc import Callable
|
|
18
15
|
from pathlib import Path
|
|
19
16
|
from typing import Annotated
|
|
20
17
|
|
|
21
18
|
import typer
|
|
22
19
|
|
|
20
|
+
from simple_module_cli._add_install import (
|
|
21
|
+
ExecRunner,
|
|
22
|
+
_default_exec_runner,
|
|
23
|
+
_fail,
|
|
24
|
+
post_install,
|
|
25
|
+
)
|
|
26
|
+
from simple_module_cli._add_sources import _add_path, _add_pypi
|
|
23
27
|
from simple_module_cli.git_source import (
|
|
24
28
|
FoundModule,
|
|
25
29
|
SpecError,
|
|
@@ -35,41 +39,15 @@ from simple_module_cli.pyproject_edit import (
|
|
|
35
39
|
save_pyproject,
|
|
36
40
|
write_dependency,
|
|
37
41
|
write_git_source,
|
|
38
|
-
write_path_source,
|
|
39
|
-
write_workspace_source,
|
|
40
42
|
)
|
|
41
|
-
from simple_module_cli.uv_workspace import is_workspace_member
|
|
42
43
|
|
|
43
44
|
__all__ = ["ExecRunner", "add_module", "post_install", "run_add"]
|
|
44
45
|
|
|
45
|
-
ExecRunner = Callable[[list[str], Path], int]
|
|
46
|
-
|
|
47
46
|
_SECURITY_NOTICE = (
|
|
48
47
|
"note: this installs and runs code from a URL you chose — review the "
|
|
49
48
|
"repository before booting the host."
|
|
50
49
|
)
|
|
51
50
|
|
|
52
|
-
_ENTRYPOINT_CHECK = (
|
|
53
|
-
"import sys\n"
|
|
54
|
-
"from importlib.metadata import distributions\n"
|
|
55
|
-
"target = sys.argv[1].replace('-', '_').lower()\n"
|
|
56
|
-
"for dist in distributions():\n"
|
|
57
|
-
" if any(ep.group == 'simple_module' for ep in dist.entry_points):\n"
|
|
58
|
-
" name = (dist.metadata['Name'] or '').replace('-', '_').lower()\n"
|
|
59
|
-
" if name == target:\n"
|
|
60
|
-
" sys.exit(0)\n"
|
|
61
|
-
"sys.exit(1)\n"
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def _default_exec_runner(cmd: list[str], cwd: Path) -> int:
|
|
66
|
-
return subprocess.run(cmd, cwd=cwd, check=False).returncode
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def _fail(message: str) -> typer.Exit:
|
|
70
|
-
typer.echo(f"ERROR: {message}", err=True)
|
|
71
|
-
return typer.Exit(code=1)
|
|
72
|
-
|
|
73
51
|
|
|
74
52
|
def _choose(
|
|
75
53
|
found: list[FoundModule],
|
|
@@ -99,97 +77,6 @@ def _choose(
|
|
|
99
77
|
]
|
|
100
78
|
|
|
101
79
|
|
|
102
|
-
def post_install(
|
|
103
|
-
project_dir: Path,
|
|
104
|
-
dists: list[str],
|
|
105
|
-
models_dists: list[str],
|
|
106
|
-
exec_runner: ExecRunner,
|
|
107
|
-
) -> None:
|
|
108
|
-
"""uv sync → gen-pages → sync-js-deps → entry-point check → reminders."""
|
|
109
|
-
if exec_runner(["uv", "sync"], project_dir) != 0:
|
|
110
|
-
raise _fail("`uv sync` failed — pyproject was written; fix and re-run `uv sync`.")
|
|
111
|
-
if (project_dir / "client_app").is_dir():
|
|
112
|
-
exec_runner(["uv", "run", "smpy", "host", "gen-pages"], project_dir)
|
|
113
|
-
exec_runner(["uv", "run", "smpy", "host", "sync-js-deps"], project_dir)
|
|
114
|
-
for dist in dists:
|
|
115
|
-
code = exec_runner(["uv", "run", "python", "-c", _ENTRYPOINT_CHECK, dist], project_dir)
|
|
116
|
-
if code != 0:
|
|
117
|
-
raise _fail(
|
|
118
|
-
f"{dist} installed but exposes no [project.entry-points.simple_module] "
|
|
119
|
-
"entry point — it is not a SimpleModule module."
|
|
120
|
-
)
|
|
121
|
-
for dist in models_dists:
|
|
122
|
-
typer.echo(
|
|
123
|
-
f"{dist} ships database models — generate and apply the migration:\n"
|
|
124
|
-
f' make migration msg="add {dist}"\n make migrate'
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
def _add_pypi(doc, pyproject: Path, raw: str, no_sync: bool, exec_runner: ExecRunner) -> list[str]:
|
|
129
|
-
name = raw
|
|
130
|
-
for i, ch in enumerate(raw):
|
|
131
|
-
if ch in "<>=!~[; ":
|
|
132
|
-
name = raw[:i]
|
|
133
|
-
break
|
|
134
|
-
constraint = raw[len(name) :]
|
|
135
|
-
write_dependency(doc, name, constraint)
|
|
136
|
-
save_pyproject(pyproject, doc)
|
|
137
|
-
typer.echo(f"Added {name} (PyPI).")
|
|
138
|
-
if not no_sync:
|
|
139
|
-
post_install(pyproject.parent, [name], [], exec_runner)
|
|
140
|
-
return [name]
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
def _resolve_local_path(project_dir: Path, spec_path: Path) -> tuple[Path, str]:
|
|
144
|
-
"""Resolve a local-path spec to ``(directory, text written to pyproject)``.
|
|
145
|
-
|
|
146
|
-
A relative path is resolved against the ``--path`` project (the pyproject
|
|
147
|
-
being edited, which is what ``[tool.uv.sources]`` paths are relative to).
|
|
148
|
-
If nothing exists there but the path exists relative to the cwd, the cwd
|
|
149
|
-
one is used and the written path is re-expressed relative to the project.
|
|
150
|
-
"""
|
|
151
|
-
if spec_path.is_absolute():
|
|
152
|
-
return spec_path, str(spec_path)
|
|
153
|
-
root = project_dir / spec_path
|
|
154
|
-
if not root.exists() and (Path.cwd() / spec_path).exists():
|
|
155
|
-
root = Path.cwd() / spec_path
|
|
156
|
-
return root, os.path.relpath(root.resolve(), project_dir.resolve())
|
|
157
|
-
return root, str(spec_path)
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
def _add_path(
|
|
161
|
-
doc, pyproject: Path, spec_path: Path, no_sync: bool, exec_runner: ExecRunner
|
|
162
|
-
) -> list[str]:
|
|
163
|
-
root, written = _resolve_local_path(pyproject.parent, spec_path)
|
|
164
|
-
found = [m for m in scan_modules(root) if m.subdirectory is None]
|
|
165
|
-
if not found:
|
|
166
|
-
raise _fail(
|
|
167
|
-
f"{root} has no [project.entry-points.simple_module] entry point at its root "
|
|
168
|
-
f"(relative paths resolve against the --path project, {pyproject.parent}, "
|
|
169
|
-
"falling back to the current directory)"
|
|
170
|
-
)
|
|
171
|
-
mod = found[0]
|
|
172
|
-
if is_workspace_member(pyproject.parent, root):
|
|
173
|
-
# uv refuses a path source for a workspace member; the scaffold's own
|
|
174
|
-
# sample module is wired the same way (unpinned + workspace = true).
|
|
175
|
-
write_dependency(doc, mod.dist_name, "")
|
|
176
|
-
write_workspace_source(doc, mod.dist_name)
|
|
177
|
-
typer.echo(f"Added {mod.dist_name} (uv workspace member {written}).")
|
|
178
|
-
else:
|
|
179
|
-
write_dependency(doc, mod.dist_name, derive_range(mod.version))
|
|
180
|
-
write_path_source(doc, mod.dist_name, path=written)
|
|
181
|
-
typer.echo(f"Added {mod.dist_name} (editable path {written}).")
|
|
182
|
-
save_pyproject(pyproject, doc)
|
|
183
|
-
if not no_sync:
|
|
184
|
-
post_install(
|
|
185
|
-
pyproject.parent,
|
|
186
|
-
[mod.dist_name],
|
|
187
|
-
[mod.dist_name] if mod.ships_models else [],
|
|
188
|
-
exec_runner,
|
|
189
|
-
)
|
|
190
|
-
return [mod.dist_name]
|
|
191
|
-
|
|
192
|
-
|
|
193
80
|
def run_add(
|
|
194
81
|
spec: str,
|
|
195
82
|
*,
|
|
@@ -19,6 +19,14 @@ from collections.abc import Sequence
|
|
|
19
19
|
from pathlib import Path
|
|
20
20
|
from typing import Any
|
|
21
21
|
|
|
22
|
+
from simple_module_cli._app_project_manifests import (
|
|
23
|
+
_APP_NPM_DEPS,
|
|
24
|
+
_APP_NPM_DEV_DEPS,
|
|
25
|
+
_APP_PY_DEV_DEPS,
|
|
26
|
+
_FRAMEWORK_VERSION,
|
|
27
|
+
_db_url,
|
|
28
|
+
_rewrite_pyproject,
|
|
29
|
+
)
|
|
22
30
|
from simple_module_cli._env import set_env_key
|
|
23
31
|
from simple_module_cli._local_env import (
|
|
24
32
|
add_postgres_password_placeholder,
|
|
@@ -35,7 +43,6 @@ from simple_module_cli.scaffolding import (
|
|
|
35
43
|
create_host,
|
|
36
44
|
create_module,
|
|
37
45
|
create_workspace,
|
|
38
|
-
resolve_framework_version,
|
|
39
46
|
)
|
|
40
47
|
|
|
41
48
|
__all__ = ["create_app_project"]
|
|
@@ -43,51 +50,6 @@ __all__ = ["create_app_project"]
|
|
|
43
50
|
_SAMPLE_MODULE_NAME = "hello"
|
|
44
51
|
_SAMPLE_MODULE_PKG = _module_to_pypi_name(_SAMPLE_MODULE_NAME)
|
|
45
52
|
|
|
46
|
-
|
|
47
|
-
_FRAMEWORK_VERSION = resolve_framework_version()
|
|
48
|
-
|
|
49
|
-
# Pin ``simple_module_cli`` as a dev dep so ``uv run smpy`` resolves to the
|
|
50
|
-
# project venv. The global ``uv tool`` install runs in its own isolated venv
|
|
51
|
-
# that can't see the project's plugin entry points (issue #134). The lint /
|
|
52
|
-
# test tooling (ruff, ty, pytest-*) backs the generated `make lint`/`make
|
|
53
|
-
# test` targets so a fresh app can run its own quality gates.
|
|
54
|
-
_APP_PY_DEV_DEPS = [
|
|
55
|
-
f"simple_module_test=={_FRAMEWORK_VERSION}",
|
|
56
|
-
f"simple_module_cli=={_FRAMEWORK_VERSION}",
|
|
57
|
-
"pytest>=8.0",
|
|
58
|
-
"pytest-asyncio>=0.24",
|
|
59
|
-
"pytest-playwright>=0.7.2",
|
|
60
|
-
# Floor: unpinned, uv backtracks playwright to 1.18 (GH #445).
|
|
61
|
-
"playwright>=1.45",
|
|
62
|
-
"ruff>=0.8",
|
|
63
|
-
"ty>=0.0.29",
|
|
64
|
-
]
|
|
65
|
-
|
|
66
|
-
_APP_NPM_DEPS = {
|
|
67
|
-
"@simple-module-py/ui": _FRAMEWORK_VERSION,
|
|
68
|
-
"@simple-module-py/i18n": _FRAMEWORK_VERSION,
|
|
69
|
-
"react": "^19.0.0",
|
|
70
|
-
"react-dom": "^19.0.0",
|
|
71
|
-
"@inertiajs/react": "^3.7.0",
|
|
72
|
-
}
|
|
73
|
-
# Flat mode has no npm workspaces, so `client_app/package.json` is never
|
|
74
|
-
# installed — the root package.json seeded from these two dicts is the only
|
|
75
|
-
# manifest npm reads. It must therefore cover everything client_app needs to
|
|
76
|
-
# build, not just the framework pins: `vite.config.ts` imports
|
|
77
|
-
# `@tailwindcss/vite`, and `tsc` needs the @types packages. Keep this in step
|
|
78
|
-
# with templates/host/client_app/package.json.tpl.
|
|
79
|
-
_APP_NPM_DEV_DEPS = {
|
|
80
|
-
"@simple-module-py/tsconfig": _FRAMEWORK_VERSION,
|
|
81
|
-
"@tailwindcss/vite": "^4.0.0",
|
|
82
|
-
"@types/node": "^26.5.0",
|
|
83
|
-
"@types/react": "^19.0.0",
|
|
84
|
-
"@types/react-dom": "^19.0.0",
|
|
85
|
-
"@vitejs/plugin-react": "^6.1.1",
|
|
86
|
-
"tailwindcss": "^4.0.0",
|
|
87
|
-
"typescript": "^7.0.2",
|
|
88
|
-
"vite": "^8.2.2",
|
|
89
|
-
}
|
|
90
|
-
|
|
91
53
|
# Files the host template ships that the workspace template re-emits at
|
|
92
54
|
# the project root. Host copies are stripped in workspace mode.
|
|
93
55
|
_HOST_FILES_OWNED_BY_WORKSPACE = (
|
|
@@ -255,46 +217,3 @@ def _seed_static_dist_placeholder(static_dist: Path) -> None:
|
|
|
255
217
|
# an empty placeholder keeps `uv sync` from failing before vite build runs.
|
|
256
218
|
static_dist.mkdir(parents=True, exist_ok=True)
|
|
257
219
|
(static_dist / ".gitkeep").touch()
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
def _db_url(db: str, db_name: str, *, flat: bool, password: str = "<POSTGRES_PASSWORD>") -> str:
|
|
261
|
-
if db == "postgres":
|
|
262
|
-
return f"postgresql+asyncpg://postgres:{password}@localhost:5432/{db_name}"
|
|
263
|
-
# Workspace mode keeps the SQLite file next to host/'s alembic.ini so
|
|
264
|
-
# `cd host && uvicorn` and `cd host && alembic` resolve the same path.
|
|
265
|
-
return "sqlite+aiosqlite:///./app.db" if flat else "sqlite+aiosqlite:///./host/app.db"
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
def _rewrite_pyproject(
|
|
269
|
-
text: str,
|
|
270
|
-
deps: list[str],
|
|
271
|
-
dev_deps: list[str],
|
|
272
|
-
*,
|
|
273
|
-
sources: Sequence[str] = (),
|
|
274
|
-
project_name: str | None = None,
|
|
275
|
-
) -> str:
|
|
276
|
-
"""Replace deps in a host ``pyproject.toml`` and pin workspace sources.
|
|
277
|
-
|
|
278
|
-
``sources`` lists ``simple_module_*`` packages that should resolve from
|
|
279
|
-
the uv workspace (``modules/*``) instead of PyPI. Emits a
|
|
280
|
-
``[tool.uv.sources]`` block per entry. Empty in flat mode.
|
|
281
|
-
|
|
282
|
-
``project_name`` overrides ``[project].name`` — set in workspace mode
|
|
283
|
-
so the host's package name differs from the workspace root's.
|
|
284
|
-
"""
|
|
285
|
-
import tomlkit
|
|
286
|
-
|
|
287
|
-
doc = tomlkit.parse(text)
|
|
288
|
-
project = doc.setdefault("project", tomlkit.table())
|
|
289
|
-
if project_name is not None:
|
|
290
|
-
project["name"] = project_name
|
|
291
|
-
project["dependencies"] = list(deps)
|
|
292
|
-
groups = doc.setdefault("dependency-groups", tomlkit.table())
|
|
293
|
-
groups["dev"] = list(dev_deps)
|
|
294
|
-
if sources:
|
|
295
|
-
tool = doc.setdefault("tool", tomlkit.table())
|
|
296
|
-
uv_table = tool.setdefault("uv", tomlkit.table())
|
|
297
|
-
uv_sources = uv_table.setdefault("sources", tomlkit.table())
|
|
298
|
-
for src in sources:
|
|
299
|
-
uv_sources[src] = {"workspace": True}
|
|
300
|
-
return tomlkit.dumps(doc)
|
|
@@ -13,6 +13,7 @@ __all__ = [
|
|
|
13
13
|
"InvalidScaffoldNameError",
|
|
14
14
|
"to_kebab_case",
|
|
15
15
|
"to_pascal_case",
|
|
16
|
+
"to_singular",
|
|
16
17
|
"to_snake_case",
|
|
17
18
|
"validate_scaffold_name",
|
|
18
19
|
]
|
|
@@ -60,6 +61,31 @@ def to_pascal_case(name: str) -> str:
|
|
|
60
61
|
return "".join(part.capitalize() for part in snake.split("_") if part)
|
|
61
62
|
|
|
62
63
|
|
|
64
|
+
_UNCOUNTABLE = frozenset({"news", "status", "series", "species", "data"})
|
|
65
|
+
_ES_SUFFIXES = ("uses", "sses", "xes", "ches", "shes")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def to_singular(snake: str) -> str:
|
|
69
|
+
"""Conservative singularization of a snake_case name: 'orders' -> 'order'.
|
|
70
|
+
|
|
71
|
+
Only the last ``_`` segment is inflected. Handles ``ies`` -> ``y`` and
|
|
72
|
+
``uses``/``xes``/``ches``/``shes``/``sses`` -> drop ``es``; leaves ``ss``/``us``/``is``
|
|
73
|
+
endings and a few uncountables alone; otherwise drops one trailing ``s``.
|
|
74
|
+
The author renames the sample entity when this guesses wrong.
|
|
75
|
+
"""
|
|
76
|
+
head, sep, word = snake.rpartition("_")
|
|
77
|
+
if word in _UNCOUNTABLE or not word.endswith("s") or word.endswith(("ss", "us", "is")):
|
|
78
|
+
return snake
|
|
79
|
+
# Irregular -ie plurals ("movies", "cookies") are not handled: "ies" -> "y".
|
|
80
|
+
if word.endswith("ies") and len(word) > 3:
|
|
81
|
+
word = word[:-3] + "y"
|
|
82
|
+
elif word.endswith(_ES_SUFFIXES):
|
|
83
|
+
word = word[:-2]
|
|
84
|
+
else:
|
|
85
|
+
word = word[:-1]
|
|
86
|
+
return head + sep + word
|
|
87
|
+
|
|
88
|
+
|
|
63
89
|
_VALID_NAME_RE = re.compile(r"^[a-z][a-z0-9]*(?:[_-][a-z0-9]+)*$")
|
|
64
90
|
|
|
65
91
|
|
|
@@ -29,6 +29,7 @@ class ModuleEntry:
|
|
|
29
29
|
|
|
30
30
|
CATALOG: dict[str, ModuleEntry] = {
|
|
31
31
|
"auth": ModuleEntry("auth", "simple_module_auth", "Auth"),
|
|
32
|
+
# ``requires`` covers each module's ``depends_on`` (pinned by a test).
|
|
32
33
|
"users": ModuleEntry("users", "simple_module_users", "Users", requires=("auth",)),
|
|
33
34
|
"permissions": ModuleEntry(
|
|
34
35
|
"permissions",
|
|
@@ -36,12 +37,7 @@ CATALOG: dict[str, ModuleEntry] = {
|
|
|
36
37
|
"Permissions",
|
|
37
38
|
requires=("auth", "users"),
|
|
38
39
|
),
|
|
39
|
-
"dashboard": ModuleEntry(
|
|
40
|
-
"dashboard",
|
|
41
|
-
"simple_module_dashboard",
|
|
42
|
-
"Dashboard",
|
|
43
|
-
requires=("users",),
|
|
44
|
-
),
|
|
40
|
+
"dashboard": ModuleEntry("dashboard", "simple_module_dashboard", "Dashboard"),
|
|
45
41
|
"settings": ModuleEntry("settings", "simple_module_settings", "Settings"),
|
|
46
42
|
"feature_flags": ModuleEntry("feature_flags", "simple_module_feature_flags", "Feature Flags"),
|
|
47
43
|
"file_storage": ModuleEntry(
|
|
@@ -54,7 +50,7 @@ CATALOG: dict[str, ModuleEntry] = {
|
|
|
54
50
|
"background_tasks",
|
|
55
51
|
"simple_module_background_tasks",
|
|
56
52
|
"Background Tasks",
|
|
57
|
-
requires=("
|
|
53
|
+
requires=("settings",),
|
|
58
54
|
recipe="background_tasks",
|
|
59
55
|
),
|
|
60
56
|
"site_lock": ModuleEntry(
|
|
@@ -22,6 +22,7 @@ import typer
|
|
|
22
22
|
|
|
23
23
|
from simple_module_cli.add_cmd import add_module, run_add
|
|
24
24
|
from simple_module_cli.case import to_kebab_case
|
|
25
|
+
from simple_module_cli.docker_cmd import docker_app
|
|
25
26
|
from simple_module_cli.module_cmd import module_app
|
|
26
27
|
from simple_module_cli.new import new_project
|
|
27
28
|
from simple_module_cli.openapi_cmd import openapi_app
|
|
@@ -44,6 +45,7 @@ app.command("new")(new_project)
|
|
|
44
45
|
app.command("add")(add_module)
|
|
45
46
|
app.command("update")(update_modules)
|
|
46
47
|
app.command("package-update")(package_update)
|
|
48
|
+
app.add_typer(docker_app, name="docker")
|
|
47
49
|
app.add_typer(skills_app, name="skills")
|
|
48
50
|
app.add_typer(module_app, name="module")
|
|
49
51
|
app.add_typer(openapi_app, name="openapi")
|
|
@@ -182,6 +184,11 @@ def create_module(
|
|
|
182
184
|
typer.echo(" npm install")
|
|
183
185
|
typer.echo(" npm run typecheck")
|
|
184
186
|
typer.echo(" uv run smpy module verify")
|
|
187
|
+
typer.echo(
|
|
188
|
+
"\nThe pages ship sample API types (pages/api.generated.ts). Once the host serves "
|
|
189
|
+
"the module, refresh them after any endpoint change:\n"
|
|
190
|
+
f" uv run smpy openapi gen-types --module {package}"
|
|
191
|
+
)
|
|
185
192
|
|
|
186
193
|
|
|
187
194
|
discover_and_mount(app)
|