@sibyllinesoft/arbiter 0.1.0

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 (302) hide show
  1. package/README.md +222 -0
  2. package/dist/cli.js +28718 -0
  3. package/dist/templates/plopfiles/__tests__/build-systems.test.ts +730 -0
  4. package/dist/templates/plopfiles/__tests__/e2e.test.ts +451 -0
  5. package/dist/templates/plopfiles/__tests__/generation.test.ts +384 -0
  6. package/dist/templates/plopfiles/__tests__/linting.test.ts +854 -0
  7. package/dist/templates/plopfiles/__tests__/modules.test.ts +278 -0
  8. package/dist/templates/plopfiles/__tests__/smoke.test.ts +619 -0
  9. package/dist/templates/plopfiles/__tests__/templates.test.ts +341 -0
  10. package/dist/templates/plopfiles/_modules/backends/go-chi/module.js +43 -0
  11. package/dist/templates/plopfiles/_modules/backends/go-chi/templates/.air.toml.hbs +30 -0
  12. package/dist/templates/plopfiles/_modules/backends/go-chi/templates/cmd/server/main.go.hbs +50 -0
  13. package/dist/templates/plopfiles/_modules/backends/go-chi/templates/go.mod.hbs +9 -0
  14. package/dist/templates/plopfiles/_modules/backends/go-chi/templates/internal/handlers/health.go.hbs +51 -0
  15. package/dist/templates/plopfiles/_modules/backends/kotlin-ktor/module.js +38 -0
  16. package/dist/templates/plopfiles/_modules/backends/kotlin-ktor/templates/build.gradle.kts.hbs +37 -0
  17. package/dist/templates/plopfiles/_modules/backends/kotlin-ktor/templates/settings.gradle.kts.hbs +1 -0
  18. package/dist/templates/plopfiles/_modules/backends/kotlin-ktor/templates/src/main/kotlin/Application.kt.hbs +44 -0
  19. package/dist/templates/plopfiles/_modules/backends/kotlin-ktor/templates/src/main/kotlin/routes/Health.kt.hbs +41 -0
  20. package/dist/templates/plopfiles/_modules/backends/kotlin-ktor/templates/src/main/resources/logback.xml.hbs +11 -0
  21. package/dist/templates/plopfiles/_modules/backends/node-express/module.js +54 -0
  22. package/dist/templates/plopfiles/_modules/backends/node-express/templates/src/index.ts.hbs +37 -0
  23. package/dist/templates/plopfiles/_modules/backends/node-express/templates/src/routes/health.ts.hbs +21 -0
  24. package/dist/templates/plopfiles/_modules/backends/node-express/templates/tsconfig.json.hbs +18 -0
  25. package/dist/templates/plopfiles/_modules/backends/node-hono/module.js +56 -0
  26. package/dist/templates/plopfiles/_modules/backends/node-hono/templates/src/index.ts.hbs +30 -0
  27. package/dist/templates/plopfiles/_modules/backends/node-hono/templates/src/routes/health.ts.hbs +64 -0
  28. package/dist/templates/plopfiles/_modules/backends/node-hono/templates/src/routes/index.ts.hbs +38 -0
  29. package/dist/templates/plopfiles/_modules/backends/node-hono/templates/tsconfig.json.hbs +20 -0
  30. package/dist/templates/plopfiles/_modules/backends/node-hono/templates/typedoc.json.hbs +25 -0
  31. package/dist/templates/plopfiles/_modules/backends/python-fastapi/module.js +65 -0
  32. package/dist/templates/plopfiles/_modules/backends/python-fastapi/templates/app/__init__.py.hbs +1 -0
  33. package/dist/templates/plopfiles/_modules/backends/python-fastapi/templates/app/config.py.hbs +28 -0
  34. package/dist/templates/plopfiles/_modules/backends/python-fastapi/templates/app/main.py.hbs +40 -0
  35. package/dist/templates/plopfiles/_modules/backends/python-fastapi/templates/app/routers/__init__.py.hbs +1 -0
  36. package/dist/templates/plopfiles/_modules/backends/python-fastapi/templates/app/routers/health.py.hbs +24 -0
  37. package/dist/templates/plopfiles/_modules/backends/python-fastapi/templates/pyproject.toml.hbs +40 -0
  38. package/dist/templates/plopfiles/_modules/backends/python-fastapi/templates/requirements.txt.hbs +6 -0
  39. package/dist/templates/plopfiles/_modules/backends/rust-axum/module.js +44 -0
  40. package/dist/templates/plopfiles/_modules/backends/rust-axum/templates/Cargo.toml.hbs +23 -0
  41. package/dist/templates/plopfiles/_modules/backends/rust-axum/templates/src/health.rs.hbs +41 -0
  42. package/dist/templates/plopfiles/_modules/backends/rust-axum/templates/src/main.rs.hbs +57 -0
  43. package/dist/templates/plopfiles/_modules/cloud/aws/module.js +33 -0
  44. package/dist/templates/plopfiles/_modules/cloud/aws/templates/aws-alb.tf.hbs +92 -0
  45. package/dist/templates/plopfiles/_modules/cloud/aws/templates/aws-ecs.tf.hbs +84 -0
  46. package/dist/templates/plopfiles/_modules/cloud/aws/templates/aws-iam.tf.hbs +60 -0
  47. package/dist/templates/plopfiles/_modules/cloud/aws/templates/aws-vpc.tf.hbs +106 -0
  48. package/dist/templates/plopfiles/_modules/cloud/aws/templates/aws.tf.hbs +56 -0
  49. package/dist/templates/plopfiles/_modules/cloud/azure/module.js +31 -0
  50. package/dist/templates/plopfiles/_modules/cloud/azure/templates/azure-container-apps.tf.hbs +99 -0
  51. package/dist/templates/plopfiles/_modules/cloud/azure/templates/azure.tf.hbs +54 -0
  52. package/dist/templates/plopfiles/_modules/cloud/cloudflare/module.js +43 -0
  53. package/dist/templates/plopfiles/_modules/cloud/cloudflare/templates/src/types.d.ts.hbs +6 -0
  54. package/dist/templates/plopfiles/_modules/cloud/cloudflare/templates/src/worker.ts.hbs +51 -0
  55. package/dist/templates/plopfiles/_modules/cloud/cloudflare/templates/wrangler.toml.hbs +36 -0
  56. package/dist/templates/plopfiles/_modules/cloud/gcp/module.js +31 -0
  57. package/dist/templates/plopfiles/_modules/cloud/gcp/templates/gcp-cloudrun.tf.hbs +89 -0
  58. package/dist/templates/plopfiles/_modules/cloud/gcp/templates/gcp.tf.hbs +62 -0
  59. package/dist/templates/plopfiles/_modules/cloud/supabase/module.js +51 -0
  60. package/dist/templates/plopfiles/_modules/cloud/supabase/templates/src/lib/supabase.ts.hbs +25 -0
  61. package/dist/templates/plopfiles/_modules/cloud/supabase/templates/src/types/supabase.ts.hbs +52 -0
  62. package/dist/templates/plopfiles/_modules/cloud/supabase/templates/supabase/config.toml.hbs +71 -0
  63. package/dist/templates/plopfiles/_modules/cloud/supabase/templates/supabase/functions/hello/index.ts.hbs +34 -0
  64. package/dist/templates/plopfiles/_modules/cloud/supabase/templates/supabase/migrations/00000000000000_init.sql.hbs +60 -0
  65. package/dist/templates/plopfiles/_modules/composer.js +180 -0
  66. package/dist/templates/plopfiles/_modules/databases/postgres-drizzle/drizzle.config.ts.hbs +10 -0
  67. package/dist/templates/plopfiles/_modules/databases/postgres-drizzle/module.js +53 -0
  68. package/dist/templates/plopfiles/_modules/databases/postgres-drizzle/templates/index.ts.hbs +15 -0
  69. package/dist/templates/plopfiles/_modules/databases/postgres-drizzle/templates/schema.ts.hbs +21 -0
  70. package/dist/templates/plopfiles/_modules/desktop/electron/module.js +51 -0
  71. package/dist/templates/plopfiles/_modules/desktop/electron/templates/forge.config.ts.hbs +38 -0
  72. package/dist/templates/plopfiles/_modules/desktop/electron/templates/package.json.hbs +22 -0
  73. package/dist/templates/plopfiles/_modules/desktop/electron/templates/src/main.ts.hbs +48 -0
  74. package/dist/templates/plopfiles/_modules/desktop/electron/templates/src/preload.ts.hbs +18 -0
  75. package/dist/templates/plopfiles/_modules/desktop/electron/templates/tsconfig.json.hbs +14 -0
  76. package/dist/templates/plopfiles/_modules/desktop/tauri/module.js +38 -0
  77. package/dist/templates/plopfiles/_modules/desktop/tauri/templates/src-tauri/Cargo.toml.hbs +20 -0
  78. package/dist/templates/plopfiles/_modules/desktop/tauri/templates/src-tauri/build.rs.hbs +3 -0
  79. package/dist/templates/plopfiles/_modules/desktop/tauri/templates/src-tauri/src/main.rs.hbs +30 -0
  80. package/dist/templates/plopfiles/_modules/desktop/tauri/templates/src-tauri/tauri.conf.json.hbs +44 -0
  81. package/dist/templates/plopfiles/_modules/docs/mkdocs/module.js +61 -0
  82. package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/docs/architecture/components.md.hbs +37 -0
  83. package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/docs/architecture/overview.md.hbs +43 -0
  84. package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/docs/contributing/development.md.hbs +164 -0
  85. package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/docs/contributing/guidelines.md.hbs +50 -0
  86. package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/docs/getting-started/configuration.md.hbs +37 -0
  87. package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/docs/getting-started/installation.md.hbs +53 -0
  88. package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/docs/getting-started/quickstart.md.hbs +39 -0
  89. package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/docs/index.md.hbs +44 -0
  90. package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/docs/javascripts/extra.js.hbs +23 -0
  91. package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/docs/stylesheets/extra.css.hbs +51 -0
  92. package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/mkdocs.yml.hbs +141 -0
  93. package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/requirements-docs.txt.hbs +15 -0
  94. package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/scripts/aggregate-docs.mjs.hbs +188 -0
  95. package/dist/templates/plopfiles/_modules/docs/mkdocs/templates/scripts/gen_ref_pages.py.hbs +54 -0
  96. package/dist/templates/plopfiles/_modules/frontends/react-vite/module.js +60 -0
  97. package/dist/templates/plopfiles/_modules/frontends/react-vite/templates/index.html.hbs +13 -0
  98. package/dist/templates/plopfiles/_modules/frontends/react-vite/templates/nginx.conf.hbs +31 -0
  99. package/dist/templates/plopfiles/_modules/frontends/react-vite/templates/postcss.config.js.hbs +6 -0
  100. package/dist/templates/plopfiles/_modules/frontends/react-vite/templates/src/App.tsx.hbs +12 -0
  101. package/dist/templates/plopfiles/_modules/frontends/react-vite/templates/src/index.css.hbs +25 -0
  102. package/dist/templates/plopfiles/_modules/frontends/react-vite/templates/src/lib/api.ts.hbs +52 -0
  103. package/dist/templates/plopfiles/_modules/frontends/react-vite/templates/src/main.tsx.hbs +18 -0
  104. package/dist/templates/plopfiles/_modules/frontends/react-vite/templates/src/pages/Home.tsx.hbs +31 -0
  105. package/dist/templates/plopfiles/_modules/frontends/react-vite/templates/tailwind.config.js.hbs +8 -0
  106. package/dist/templates/plopfiles/_modules/frontends/react-vite/templates/tsconfig.json.hbs +25 -0
  107. package/dist/templates/plopfiles/_modules/frontends/react-vite/templates/tsconfig.node.json.hbs +10 -0
  108. package/dist/templates/plopfiles/_modules/frontends/react-vite/templates/vite.config.ts.hbs +21 -0
  109. package/dist/templates/plopfiles/_modules/frontends/solid-vite/module.js +51 -0
  110. package/dist/templates/plopfiles/_modules/frontends/solid-vite/templates/index.html.hbs +13 -0
  111. package/dist/templates/plopfiles/_modules/frontends/solid-vite/templates/postcss.config.js.hbs +6 -0
  112. package/dist/templates/plopfiles/_modules/frontends/solid-vite/templates/src/App.tsx.hbs +12 -0
  113. package/dist/templates/plopfiles/_modules/frontends/solid-vite/templates/src/index.css.hbs +3 -0
  114. package/dist/templates/plopfiles/_modules/frontends/solid-vite/templates/src/index.tsx.hbs +21 -0
  115. package/dist/templates/plopfiles/_modules/frontends/solid-vite/templates/src/lib/api.ts.hbs +52 -0
  116. package/dist/templates/plopfiles/_modules/frontends/solid-vite/templates/src/pages/Home.tsx.hbs +34 -0
  117. package/dist/templates/plopfiles/_modules/frontends/solid-vite/templates/tailwind.config.js.hbs +8 -0
  118. package/dist/templates/plopfiles/_modules/frontends/solid-vite/templates/tsconfig.json.hbs +24 -0
  119. package/dist/templates/plopfiles/_modules/frontends/solid-vite/templates/vite.config.ts.hbs +21 -0
  120. package/dist/templates/plopfiles/_modules/frontends/vue-vite/module.js +54 -0
  121. package/dist/templates/plopfiles/_modules/frontends/vue-vite/templates/index.html.hbs +13 -0
  122. package/dist/templates/plopfiles/_modules/frontends/vue-vite/templates/postcss.config.js.hbs +6 -0
  123. package/dist/templates/plopfiles/_modules/frontends/vue-vite/templates/src/App.vue.hbs +9 -0
  124. package/dist/templates/plopfiles/_modules/frontends/vue-vite/templates/src/index.css.hbs +3 -0
  125. package/dist/templates/plopfiles/_modules/frontends/vue-vite/templates/src/lib/api.ts.hbs +52 -0
  126. package/dist/templates/plopfiles/_modules/frontends/vue-vite/templates/src/main.ts.hbs +14 -0
  127. package/dist/templates/plopfiles/_modules/frontends/vue-vite/templates/src/pages/Home.vue.hbs +27 -0
  128. package/dist/templates/plopfiles/_modules/frontends/vue-vite/templates/src/router.ts.hbs +13 -0
  129. package/dist/templates/plopfiles/_modules/frontends/vue-vite/templates/tailwind.config.js.hbs +8 -0
  130. package/dist/templates/plopfiles/_modules/frontends/vue-vite/templates/tsconfig.json.hbs +25 -0
  131. package/dist/templates/plopfiles/_modules/frontends/vue-vite/templates/vite.config.ts.hbs +21 -0
  132. package/dist/templates/plopfiles/_modules/infra/docker-compose/module.js +52 -0
  133. package/dist/templates/plopfiles/_modules/infra/docker-compose/templates/Dockerfile.backend.hbs +32 -0
  134. package/dist/templates/plopfiles/_modules/infra/docker-compose/templates/Dockerfile.frontend.hbs +31 -0
  135. package/dist/templates/plopfiles/_modules/infra/docker-compose/templates/docker-compose.yml.hbs +97 -0
  136. package/dist/templates/plopfiles/_modules/infra/github-actions/module.js +24 -0
  137. package/dist/templates/plopfiles/_modules/infra/github-actions/templates/.github/workflows/ci.yml.hbs +78 -0
  138. package/dist/templates/plopfiles/_modules/infra/github-actions/templates/.github/workflows/deploy.yml.hbs +60 -0
  139. package/dist/templates/plopfiles/_modules/infra/just/module.js +26 -0
  140. package/dist/templates/plopfiles/_modules/infra/just/templates/justfile.hbs +205 -0
  141. package/dist/templates/plopfiles/_modules/infra/kubernetes/module.js +30 -0
  142. package/dist/templates/plopfiles/_modules/infra/kubernetes/templates/k8s/base/deployment.yaml.hbs +48 -0
  143. package/dist/templates/plopfiles/_modules/infra/kubernetes/templates/k8s/base/ingress.yaml.hbs +23 -0
  144. package/dist/templates/plopfiles/_modules/infra/kubernetes/templates/k8s/base/kustomization.yaml.hbs +11 -0
  145. package/dist/templates/plopfiles/_modules/infra/kubernetes/templates/k8s/base/service.yaml.hbs +15 -0
  146. package/dist/templates/plopfiles/_modules/infra/kubernetes/templates/k8s/overlays/dev/deployment-patch.yaml.hbs +17 -0
  147. package/dist/templates/plopfiles/_modules/infra/kubernetes/templates/k8s/overlays/dev/kustomization.yaml.hbs +15 -0
  148. package/dist/templates/plopfiles/_modules/infra/kubernetes/templates/k8s/overlays/prod/deployment-patch.yaml.hbs +17 -0
  149. package/dist/templates/plopfiles/_modules/infra/kubernetes/templates/k8s/overlays/prod/kustomization.yaml.hbs +15 -0
  150. package/dist/templates/plopfiles/_modules/infra/pulumi/module.js +30 -0
  151. package/dist/templates/plopfiles/_modules/infra/pulumi/templates/.gitignore.hbs +5 -0
  152. package/dist/templates/plopfiles/_modules/infra/pulumi/templates/Pulumi.dev.yaml.hbs +3 -0
  153. package/dist/templates/plopfiles/_modules/infra/pulumi/templates/Pulumi.yaml.hbs +7 -0
  154. package/dist/templates/plopfiles/_modules/infra/pulumi/templates/index.ts.hbs +26 -0
  155. package/dist/templates/plopfiles/_modules/infra/pulumi/templates/package.json.hbs +12 -0
  156. package/dist/templates/plopfiles/_modules/infra/pulumi/templates/tsconfig.json.hbs +16 -0
  157. package/dist/templates/plopfiles/_modules/infra/terraform/module.js +31 -0
  158. package/dist/templates/plopfiles/_modules/infra/terraform/templates/.gitignore.hbs +12 -0
  159. package/dist/templates/plopfiles/_modules/infra/terraform/templates/main.tf.hbs +34 -0
  160. package/dist/templates/plopfiles/_modules/infra/terraform/templates/outputs.tf.hbs +15 -0
  161. package/dist/templates/plopfiles/_modules/infra/terraform/templates/terraform.tfvars.example.hbs +6 -0
  162. package/dist/templates/plopfiles/_modules/infra/terraform/templates/variables.tf.hbs +30 -0
  163. package/dist/templates/plopfiles/_modules/mobile/android-kotlin/module.js +38 -0
  164. package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/app/build.gradle.kts.hbs +102 -0
  165. package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/app/proguard-rules.pro.hbs +20 -0
  166. package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/app/src/main/AndroidManifest.xml.hbs +25 -0
  167. package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/app/src/main/kotlin/com/example/app/ApiClient.kt.hbs +38 -0
  168. package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/app/src/main/kotlin/com/example/app/App.kt.hbs +116 -0
  169. package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/app/src/main/kotlin/com/example/app/MainActivity.kt.hbs +28 -0
  170. package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/app/src/main/kotlin/com/example/app/ui/theme/Color.kt.hbs +11 -0
  171. package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/app/src/main/kotlin/com/example/app/ui/theme/Theme.kt.hbs +55 -0
  172. package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/app/src/main/kotlin/com/example/app/ui/theme/Type.kt.hbs +31 -0
  173. package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/app/src/main/res/values/strings.xml.hbs +3 -0
  174. package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/app/src/main/res/values/themes.xml.hbs +4 -0
  175. package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/build.gradle.kts.hbs +6 -0
  176. package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/gradle.properties.hbs +4 -0
  177. package/dist/templates/plopfiles/_modules/mobile/android-kotlin/templates/settings.gradle.kts.hbs +18 -0
  178. package/dist/templates/plopfiles/_modules/mobile/flutter/module.js +36 -0
  179. package/dist/templates/plopfiles/_modules/mobile/flutter/templates/.env.hbs +1 -0
  180. package/dist/templates/plopfiles/_modules/mobile/flutter/templates/lib/main.dart.hbs +29 -0
  181. package/dist/templates/plopfiles/_modules/mobile/flutter/templates/lib/screens/home_screen.dart.hbs +88 -0
  182. package/dist/templates/plopfiles/_modules/mobile/flutter/templates/lib/services/api_service.dart.hbs +61 -0
  183. package/dist/templates/plopfiles/_modules/mobile/flutter/templates/pubspec.yaml.hbs +25 -0
  184. package/dist/templates/plopfiles/_modules/mobile/ios-swift/module.js +39 -0
  185. package/dist/templates/plopfiles/_modules/mobile/ios-swift/templates/App/App.swift.hbs +10 -0
  186. package/dist/templates/plopfiles/_modules/mobile/ios-swift/templates/App/Assets.xcassets/AccentColor.colorset/Contents.json.hbs +11 -0
  187. package/dist/templates/plopfiles/_modules/mobile/ios-swift/templates/App/Assets.xcassets/AppIcon.appiconset/Contents.json.hbs +13 -0
  188. package/dist/templates/plopfiles/_modules/mobile/ios-swift/templates/App/Assets.xcassets/Contents.json.hbs +6 -0
  189. package/dist/templates/plopfiles/_modules/mobile/ios-swift/templates/App/ContentView.swift.hbs +13 -0
  190. package/dist/templates/plopfiles/_modules/mobile/ios-swift/templates/App/Services/APIClient.swift.hbs +58 -0
  191. package/dist/templates/plopfiles/_modules/mobile/ios-swift/templates/App/Views/DetailsView.swift.hbs +22 -0
  192. package/dist/templates/plopfiles/_modules/mobile/ios-swift/templates/App/Views/HomeView.swift.hbs +62 -0
  193. package/dist/templates/plopfiles/_modules/mobile/ios-swift/templates/App.xcodeproj/project.pbxproj.hbs +363 -0
  194. package/dist/templates/plopfiles/_modules/mobile/react-native/module.js +36 -0
  195. package/dist/templates/plopfiles/_modules/mobile/react-native/templates/app/_layout.tsx.hbs +16 -0
  196. package/dist/templates/plopfiles/_modules/mobile/react-native/templates/app/index.tsx.hbs +66 -0
  197. package/dist/templates/plopfiles/_modules/mobile/react-native/templates/app.json.hbs +37 -0
  198. package/dist/templates/plopfiles/_modules/mobile/react-native/templates/package.json.hbs +29 -0
  199. package/dist/templates/plopfiles/_modules/mobile/react-native/templates/src/lib/api.ts.hbs +52 -0
  200. package/dist/templates/plopfiles/_modules/mobile/react-native/templates/tsconfig.json.hbs +11 -0
  201. package/dist/templates/plopfiles/_modules/quality/biome/module.js +44 -0
  202. package/dist/templates/plopfiles/_modules/quality/biome/templates/biome.json.hbs +126 -0
  203. package/dist/templates/plopfiles/_modules/quality/clippy/module.js +40 -0
  204. package/dist/templates/plopfiles/_modules/quality/clippy/templates/clippy.toml.hbs +29 -0
  205. package/dist/templates/plopfiles/_modules/quality/clippy/templates/rustfmt.toml.hbs +44 -0
  206. package/dist/templates/plopfiles/_modules/quality/golangci-lint/module.js +40 -0
  207. package/dist/templates/plopfiles/_modules/quality/golangci-lint/templates/.golangci.yml.hbs +199 -0
  208. package/dist/templates/plopfiles/_modules/quality/ruff/module.js +43 -0
  209. package/dist/templates/plopfiles/_modules/quality/ruff/templates/mypy.ini.hbs +48 -0
  210. package/dist/templates/plopfiles/_modules/quality/ruff/templates/ruff.toml.hbs +111 -0
  211. package/dist/templates/plopfiles/_modules/storybook/react-storybook/module.js +54 -0
  212. package/dist/templates/plopfiles/_modules/storybook/react-storybook/templates/.storybook/main.ts.hbs +44 -0
  213. package/dist/templates/plopfiles/_modules/storybook/react-storybook/templates/.storybook/preview.ts.hbs +68 -0
  214. package/dist/templates/plopfiles/_modules/storybook/react-storybook/templates/src/stories/Button.stories.tsx.hbs +158 -0
  215. package/dist/templates/plopfiles/_modules/types.ts +98 -0
  216. package/dist/templates/plopfiles/full-stack/plopfile.js +132 -0
  217. package/dist/templates/plopfiles/full-stack/templates/gitignore.hbs +44 -0
  218. package/dist/templates/plopfiles/full-stack/templates/package.json.hbs +21 -0
  219. package/dist/templates/plopfiles/presets/cli-go/module.js +43 -0
  220. package/dist/templates/plopfiles/presets/cli-go/templates/README.md.hbs +42 -0
  221. package/dist/templates/plopfiles/presets/cli-go/templates/config.json.hbs +9 -0
  222. package/dist/templates/plopfiles/presets/cli-node/module.js +45 -0
  223. package/dist/templates/plopfiles/presets/cli-node/templates/README.md.hbs +42 -0
  224. package/dist/templates/plopfiles/presets/cli-node/templates/config.json.hbs +9 -0
  225. package/dist/templates/plopfiles/presets/cli-python/module.js +43 -0
  226. package/dist/templates/plopfiles/presets/cli-python/templates/README.md.hbs +42 -0
  227. package/dist/templates/plopfiles/presets/cli-python/templates/config.json.hbs +9 -0
  228. package/dist/templates/plopfiles/presets/cli-rust/module.js +43 -0
  229. package/dist/templates/plopfiles/presets/cli-rust/templates/README.md.hbs +42 -0
  230. package/dist/templates/plopfiles/presets/cli-rust/templates/config.json.hbs +9 -0
  231. package/dist/templates/plopfiles/presets/loader.js +125 -0
  232. package/dist/templates/plopfiles/typescript-service/plopfile.js +186 -0
  233. package/dist/templates/plopfiles/typescript-service/templates/Dockerfile.hbs +29 -0
  234. package/dist/templates/plopfiles/typescript-service/templates/README.md.hbs +85 -0
  235. package/dist/templates/plopfiles/typescript-service/templates/config.ts.hbs +15 -0
  236. package/dist/templates/plopfiles/typescript-service/templates/db/index.ts.hbs +19 -0
  237. package/dist/templates/plopfiles/typescript-service/templates/db/schema.ts.hbs +16 -0
  238. package/dist/templates/plopfiles/typescript-service/templates/index.ts.hbs +89 -0
  239. package/dist/templates/plopfiles/typescript-service/templates/package.json.hbs +36 -0
  240. package/dist/templates/plopfiles/typescript-service/templates/routes/health.ts.hbs +61 -0
  241. package/dist/templates/plopfiles/typescript-service/templates/routes/index.ts.hbs +35 -0
  242. package/dist/templates/plopfiles/typescript-service/templates/tsconfig.json.hbs +24 -0
  243. package/dist/templates/plopfiles/typescript-service/templates/types.ts.hbs +28 -0
  244. package/dist/templates/typescript/component.module.css.tpl +6 -0
  245. package/dist/templates/typescript/component.test.tsx.tpl +9 -0
  246. package/dist/templates/typescript/component.tsx.tpl +17 -0
  247. package/dist/templates/typescript/component.types.ts.tpl +3 -0
  248. package/dist/templates/typescript/project/nextjs/app/globals.css.tpl +10 -0
  249. package/dist/templates/typescript/project/nextjs/app/layout.tsx.tpl +15 -0
  250. package/dist/templates/typescript/project/nextjs/app/page.tsx.tpl +8 -0
  251. package/dist/templates/typescript/project/nextjs/babel.config.js.tpl +8 -0
  252. package/dist/templates/typescript/project/nextjs/jest.config.js.tpl +10 -0
  253. package/dist/templates/typescript/project/nextjs/jest.setup.ts.tpl +6 -0
  254. package/dist/templates/typescript/project/nextjs/next-env.d.ts.tpl +5 -0
  255. package/dist/templates/typescript/project/nextjs/next.config.js.tpl +9 -0
  256. package/dist/templates/typescript/project/nextjs/package.json.tpl +1 -0
  257. package/dist/templates/typescript/project/nextjs/tsconfig.json.tpl +1 -0
  258. package/dist/templates/typescript/project/vite/App.css.tpl +8 -0
  259. package/dist/templates/typescript/project/vite/App.tsx.tpl +17 -0
  260. package/dist/templates/typescript/project/vite/index.css.tpl +8 -0
  261. package/dist/templates/typescript/project/vite/index.html.tpl +13 -0
  262. package/dist/templates/typescript/project/vite/main.tsx.tpl +10 -0
  263. package/dist/templates/typescript/project/vite/package.json.tpl +1 -0
  264. package/dist/templates/typescript/project/vite/test-setup.ts.tpl +1 -0
  265. package/dist/templates/typescript/project/vite/tsconfig.build.json.tpl +1 -0
  266. package/dist/templates/typescript/project/vite/tsconfig.json.tpl +1 -0
  267. package/dist/templates/typescript/project/vite/tsconfig.node.json.tpl +1 -0
  268. package/dist/templates/typescript/project/vite/vite.config.ts.tpl +18 -0
  269. package/dist/templates/typescript/service.api.ts.tpl +27 -0
  270. package/dist/templates/typescript/service.class.ts.tpl +28 -0
  271. package/dist/templates/typescript/service.handler.ts.tpl +42 -0
  272. package/dist/templates/typescript/service.schema.ts.tpl +16 -0
  273. package/package.json +95 -0
  274. package/templates/typescript/component.module.css.tpl +6 -0
  275. package/templates/typescript/component.test.tsx.tpl +9 -0
  276. package/templates/typescript/component.tsx.tpl +17 -0
  277. package/templates/typescript/component.types.ts.tpl +3 -0
  278. package/templates/typescript/project/nextjs/app/globals.css.tpl +10 -0
  279. package/templates/typescript/project/nextjs/app/layout.tsx.tpl +15 -0
  280. package/templates/typescript/project/nextjs/app/page.tsx.tpl +8 -0
  281. package/templates/typescript/project/nextjs/babel.config.js.tpl +8 -0
  282. package/templates/typescript/project/nextjs/jest.config.js.tpl +10 -0
  283. package/templates/typescript/project/nextjs/jest.setup.ts.tpl +6 -0
  284. package/templates/typescript/project/nextjs/next-env.d.ts.tpl +5 -0
  285. package/templates/typescript/project/nextjs/next.config.js.tpl +9 -0
  286. package/templates/typescript/project/nextjs/package.json.tpl +1 -0
  287. package/templates/typescript/project/nextjs/tsconfig.json.tpl +1 -0
  288. package/templates/typescript/project/vite/App.css.tpl +8 -0
  289. package/templates/typescript/project/vite/App.tsx.tpl +17 -0
  290. package/templates/typescript/project/vite/index.css.tpl +8 -0
  291. package/templates/typescript/project/vite/index.html.tpl +13 -0
  292. package/templates/typescript/project/vite/main.tsx.tpl +10 -0
  293. package/templates/typescript/project/vite/package.json.tpl +1 -0
  294. package/templates/typescript/project/vite/test-setup.ts.tpl +1 -0
  295. package/templates/typescript/project/vite/tsconfig.build.json.tpl +1 -0
  296. package/templates/typescript/project/vite/tsconfig.json.tpl +1 -0
  297. package/templates/typescript/project/vite/tsconfig.node.json.tpl +1 -0
  298. package/templates/typescript/project/vite/vite.config.ts.tpl +18 -0
  299. package/templates/typescript/service.api.ts.tpl +27 -0
  300. package/templates/typescript/service.class.ts.tpl +28 -0
  301. package/templates/typescript/service.handler.ts.tpl +42 -0
  302. package/templates/typescript/service.schema.ts.tpl +16 -0
@@ -0,0 +1,730 @@
1
+ /**
2
+ * Build Systems Tests
3
+ *
4
+ * Comprehensive tests for Bazel, Nx, and Turborepo template modules.
5
+ * Includes generation tests, linting, and E2E validation where possible.
6
+ *
7
+ * Run with: bun test build-systems.test.ts
8
+ */
9
+
10
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
11
+ import { tmpdir } from "os";
12
+ import { dirname, join } from "path";
13
+ import { fileURLToPath } from "url";
14
+ import { $ } from "bun";
15
+ import { mkdir, mkdtemp, readFile, readdir, rm } from "fs/promises";
16
+ import nodePlop from "node-plop";
17
+ import { loadModule } from "../_modules/composer.js";
18
+
19
+ const __dirname = dirname(fileURLToPath(import.meta.url));
20
+
21
+ const TEST_TIMEOUT = 60_000;
22
+
23
+ // Helper to execute plop actions
24
+ async function executePlopActions(
25
+ destPath: string,
26
+ actions: Array<Record<string, unknown>>,
27
+ context: Record<string, unknown>,
28
+ ) {
29
+ const plop = await nodePlop(undefined, {
30
+ destBasePath: destPath,
31
+ force: true,
32
+ });
33
+
34
+ // Register helpers
35
+ plop.setHelper(
36
+ "kebabCase",
37
+ (str: string) =>
38
+ str
39
+ ?.toLowerCase()
40
+ .replace(/\s+/g, "-")
41
+ .replace(/[^a-z0-9-]/g, "") || "",
42
+ );
43
+ plop.setHelper(
44
+ "snakeCase",
45
+ (str: string) =>
46
+ str
47
+ ?.toLowerCase()
48
+ .replace(/[\s-]+/g, "_")
49
+ .replace(/[^a-z0-9_]/g, "") || "",
50
+ );
51
+ plop.setHelper("camelCase", (str: string) => {
52
+ if (!str) return "";
53
+ return str.toLowerCase().replace(/[^a-zA-Z0-9]+(.)/g, (_, chr: string) => chr.toUpperCase());
54
+ });
55
+ plop.setHelper("pascalCase", (str: string) => {
56
+ if (!str) return "";
57
+ const camel = str
58
+ .toLowerCase()
59
+ .replace(/[^a-zA-Z0-9]+(.)/g, (_, chr: string) => chr.toUpperCase());
60
+ return camel.charAt(0).toUpperCase() + camel.slice(1);
61
+ });
62
+ plop.setHelper(
63
+ "titleCase",
64
+ (str: string) => str?.replace(/\b\w/g, (char) => char.toUpperCase()) || "",
65
+ );
66
+ plop.setHelper("eq", (a: unknown, b: unknown) => a === b);
67
+ plop.setHelper("ne", (a: unknown, b: unknown) => a !== b);
68
+ plop.setHelper(
69
+ "has",
70
+ (arr: unknown[], item: unknown) => Array.isArray(arr) && arr.includes(item),
71
+ );
72
+ plop.setHelper("json", (obj: unknown) => JSON.stringify(obj, null, 2));
73
+
74
+ plop.setGenerator("temp", {
75
+ description: "temp",
76
+ prompts: [],
77
+ actions: actions as any[],
78
+ });
79
+
80
+ const gen = plop.getGenerator("temp");
81
+ const results = await gen.runActions(context);
82
+
83
+ if (results.failures && results.failures.length > 0) {
84
+ const errors = results.failures.map((f) => f.error || f.message || String(f)).join("\n");
85
+ throw new Error(`Generation failed:\n${errors}`);
86
+ }
87
+
88
+ return results;
89
+ }
90
+
91
+ async function generateModule(
92
+ category: string,
93
+ moduleName: string,
94
+ destPath: string,
95
+ context: Record<string, unknown>,
96
+ ) {
97
+ const mod = await loadModule(category, moduleName);
98
+ const actions = mod.default(context);
99
+ await executePlopActions(destPath, actions, context);
100
+ return mod;
101
+ }
102
+
103
+ async function commandExists(cmd: string): Promise<boolean> {
104
+ try {
105
+ await $`which ${cmd}`.quiet();
106
+ return true;
107
+ } catch {
108
+ return false;
109
+ }
110
+ }
111
+
112
+ // Track available tools
113
+ const tools: Record<string, boolean> = {};
114
+
115
+ beforeAll(async () => {
116
+ tools.bazel = await commandExists("bazel");
117
+ tools.buildifier = await commandExists("buildifier");
118
+ tools.nx = await commandExists("nx");
119
+ tools.turbo = await commandExists("turbo");
120
+
121
+ console.log(
122
+ "Available build tools:",
123
+ Object.entries(tools)
124
+ .filter(([_, v]) => v)
125
+ .map(([k]) => k)
126
+ .join(", ") || "none",
127
+ );
128
+ });
129
+
130
+ describe("Bazel Module", () => {
131
+ let tempDir: string;
132
+
133
+ beforeAll(async () => {
134
+ tempDir = await mkdtemp(join(tmpdir(), "arbiter-bazel-"));
135
+ });
136
+
137
+ afterAll(async () => {
138
+ await rm(tempDir, { recursive: true, force: true });
139
+ });
140
+
141
+ test("generates repo-level configs for Node.js backend", async () => {
142
+ const projectDir = join(tempDir, "node-backend");
143
+ await mkdir(projectDir, { recursive: true });
144
+ await mkdir(join(projectDir, "backend"), { recursive: true });
145
+
146
+ const context = {
147
+ name: "node-app",
148
+ projectDir,
149
+ backendDir: "backend",
150
+ backend: "node-hono",
151
+ frontend: "none",
152
+ };
153
+
154
+ await generateModule("build", "bazel", projectDir, context);
155
+
156
+ // Check repo-level files exist
157
+ const files = await readdir(projectDir);
158
+ expect(files).toContain("WORKSPACE.bazel");
159
+ expect(files).toContain(".bazelrc");
160
+ expect(files).toContain(".bazelignore");
161
+ expect(files).toContain("BUILD.bazel");
162
+
163
+ // Check backend BUILD file
164
+ const backendFiles = await readdir(join(projectDir, "backend"));
165
+ expect(backendFiles).toContain("BUILD.bazel");
166
+
167
+ // Validate WORKSPACE content
168
+ const workspaceContent = await readFile(join(projectDir, "WORKSPACE.bazel"), "utf-8");
169
+ expect(workspaceContent).toContain('workspace(name = "node_app")');
170
+ expect(workspaceContent).toContain("aspect_rules_js");
171
+ expect(workspaceContent).toContain("aspect_rules_ts");
172
+ });
173
+
174
+ test("generates repo-level configs for Go backend", async () => {
175
+ const projectDir = join(tempDir, "go-backend");
176
+ await mkdir(projectDir, { recursive: true });
177
+ await mkdir(join(projectDir, "backend"), { recursive: true });
178
+
179
+ const context = {
180
+ name: "go-app",
181
+ projectDir,
182
+ backendDir: "backend",
183
+ backend: "go-chi",
184
+ frontend: "none",
185
+ };
186
+
187
+ await generateModule("build", "bazel", projectDir, context);
188
+
189
+ const workspaceContent = await readFile(join(projectDir, "WORKSPACE.bazel"), "utf-8");
190
+ expect(workspaceContent).toContain("io_bazel_rules_go");
191
+ expect(workspaceContent).toContain("bazel_gazelle");
192
+
193
+ const backendBuildContent = await readFile(join(projectDir, "backend/BUILD.bazel"), "utf-8");
194
+ expect(backendBuildContent).toContain("go_binary");
195
+ expect(backendBuildContent).toContain("go_library");
196
+ });
197
+
198
+ test("generates repo-level configs for Python backend", async () => {
199
+ const projectDir = join(tempDir, "python-backend");
200
+ await mkdir(projectDir, { recursive: true });
201
+ await mkdir(join(projectDir, "backend"), { recursive: true });
202
+
203
+ const context = {
204
+ name: "python-app",
205
+ projectDir,
206
+ backendDir: "backend",
207
+ backend: "python-fastapi",
208
+ frontend: "none",
209
+ };
210
+
211
+ await generateModule("build", "bazel", projectDir, context);
212
+
213
+ const workspaceContent = await readFile(join(projectDir, "WORKSPACE.bazel"), "utf-8");
214
+ expect(workspaceContent).toContain("rules_python");
215
+
216
+ const backendBuildContent = await readFile(join(projectDir, "backend/BUILD.bazel"), "utf-8");
217
+ expect(backendBuildContent).toContain("py_binary");
218
+ expect(backendBuildContent).toContain("py_library");
219
+ });
220
+
221
+ test("generates repo-level configs for Rust backend", async () => {
222
+ const projectDir = join(tempDir, "rust-backend");
223
+ await mkdir(projectDir, { recursive: true });
224
+ await mkdir(join(projectDir, "backend"), { recursive: true });
225
+
226
+ const context = {
227
+ name: "rust-app",
228
+ projectDir,
229
+ backendDir: "backend",
230
+ backend: "rust-axum",
231
+ frontend: "none",
232
+ };
233
+
234
+ await generateModule("build", "bazel", projectDir, context);
235
+
236
+ const workspaceContent = await readFile(join(projectDir, "WORKSPACE.bazel"), "utf-8");
237
+ expect(workspaceContent).toContain("rules_rust");
238
+
239
+ const backendBuildContent = await readFile(join(projectDir, "backend/BUILD.bazel"), "utf-8");
240
+ expect(backendBuildContent).toContain("rust_binary");
241
+ expect(backendBuildContent).toContain("rust_library");
242
+ });
243
+
244
+ test("generates frontend BUILD file for React", async () => {
245
+ const projectDir = join(tempDir, "full-stack");
246
+ await mkdir(projectDir, { recursive: true });
247
+ await mkdir(join(projectDir, "backend"), { recursive: true });
248
+ await mkdir(join(projectDir, "frontend"), { recursive: true });
249
+
250
+ const context = {
251
+ name: "full-stack-app",
252
+ projectDir,
253
+ backendDir: "backend",
254
+ frontendDir: "frontend",
255
+ backend: "node-hono",
256
+ frontend: "react-vite",
257
+ };
258
+
259
+ await generateModule("build", "bazel", projectDir, context);
260
+
261
+ const frontendBuildContent = await readFile(join(projectDir, "frontend/BUILD.bazel"), "utf-8");
262
+ expect(frontendBuildContent).toContain("ts_project");
263
+ expect(frontendBuildContent).toContain("js_run_binary");
264
+ });
265
+
266
+ test(
267
+ "buildifier validates generated files",
268
+ async () => {
269
+ if (!tools.buildifier) {
270
+ console.log("Skipping: buildifier not available");
271
+ return;
272
+ }
273
+
274
+ const projectDir = join(tempDir, "buildifier-test");
275
+ await mkdir(projectDir, { recursive: true });
276
+ await mkdir(join(projectDir, "backend"), { recursive: true });
277
+
278
+ const context = {
279
+ name: "buildifier-test",
280
+ projectDir,
281
+ backendDir: "backend",
282
+ backend: "go-chi",
283
+ frontend: "none",
284
+ };
285
+
286
+ await generateModule("build", "bazel", projectDir, context);
287
+
288
+ // Run buildifier in check mode
289
+ const result =
290
+ await $`buildifier --lint=warn --mode=check ${join(projectDir, "WORKSPACE.bazel")} ${join(projectDir, "BUILD.bazel")} ${join(projectDir, "backend/BUILD.bazel")}`.nothrow();
291
+
292
+ // 0 = valid, 4 = lint warnings (acceptable)
293
+ expect(result.exitCode === 0 || result.exitCode === 4).toBe(true);
294
+ },
295
+ TEST_TIMEOUT,
296
+ );
297
+ });
298
+
299
+ describe("Nx Module", () => {
300
+ let tempDir: string;
301
+
302
+ beforeAll(async () => {
303
+ tempDir = await mkdtemp(join(tmpdir(), "arbiter-nx-"));
304
+ });
305
+
306
+ afterAll(async () => {
307
+ await rm(tempDir, { recursive: true, force: true });
308
+ });
309
+
310
+ test("generates repo-level nx.json", async () => {
311
+ const projectDir = join(tempDir, "repo-level");
312
+ await mkdir(projectDir, { recursive: true });
313
+
314
+ const context = {
315
+ name: "nx-app",
316
+ projectDir,
317
+ backend: "none",
318
+ frontend: "none",
319
+ };
320
+
321
+ await generateModule("build", "nx", projectDir, context);
322
+
323
+ const files = await readdir(projectDir);
324
+ expect(files).toContain("nx.json");
325
+ expect(files).toContain(".nxignore");
326
+
327
+ const nxJson = JSON.parse(await readFile(join(projectDir, "nx.json"), "utf-8"));
328
+ expect(nxJson.$schema).toBeDefined();
329
+ expect(nxJson.targetDefaults).toBeDefined();
330
+ expect(nxJson.namedInputs).toBeDefined();
331
+ });
332
+
333
+ test("generates project.json for Node.js backend", async () => {
334
+ const projectDir = join(tempDir, "node-backend");
335
+ await mkdir(projectDir, { recursive: true });
336
+ await mkdir(join(projectDir, "backend"), { recursive: true });
337
+
338
+ const context = {
339
+ name: "nx-node-app",
340
+ projectDir,
341
+ backendDir: "backend",
342
+ backend: "node-hono",
343
+ frontend: "none",
344
+ };
345
+
346
+ await generateModule("build", "nx", projectDir, context);
347
+
348
+ const projectJson = JSON.parse(
349
+ await readFile(join(projectDir, "backend/project.json"), "utf-8"),
350
+ );
351
+
352
+ expect(projectJson.name).toBe("nx-node-app-backend");
353
+ expect(projectJson.projectType).toBe("application");
354
+ expect(projectJson.targets.build).toBeDefined();
355
+ expect(projectJson.targets.serve).toBeDefined();
356
+ expect(projectJson.targets.test).toBeDefined();
357
+ expect(projectJson.targets.lint).toBeDefined();
358
+ });
359
+
360
+ test("generates project.json for Go backend", async () => {
361
+ const projectDir = join(tempDir, "go-backend");
362
+ await mkdir(projectDir, { recursive: true });
363
+ await mkdir(join(projectDir, "backend"), { recursive: true });
364
+
365
+ const context = {
366
+ name: "nx-go-app",
367
+ projectDir,
368
+ backendDir: "backend",
369
+ backend: "go-chi",
370
+ frontend: "none",
371
+ };
372
+
373
+ await generateModule("build", "nx", projectDir, context);
374
+
375
+ const projectJson = JSON.parse(
376
+ await readFile(join(projectDir, "backend/project.json"), "utf-8"),
377
+ );
378
+
379
+ expect(projectJson.targets.build.executor).toContain("nx-go");
380
+ expect(projectJson.targets.test.executor).toContain("nx-go");
381
+ });
382
+
383
+ test("generates project.json for Python backend", async () => {
384
+ const projectDir = join(tempDir, "python-backend");
385
+ await mkdir(projectDir, { recursive: true });
386
+ await mkdir(join(projectDir, "backend"), { recursive: true });
387
+
388
+ const context = {
389
+ name: "nx-python-app",
390
+ projectDir,
391
+ backendDir: "backend",
392
+ backend: "python-fastapi",
393
+ frontend: "none",
394
+ };
395
+
396
+ await generateModule("build", "nx", projectDir, context);
397
+
398
+ const projectJson = JSON.parse(
399
+ await readFile(join(projectDir, "backend/project.json"), "utf-8"),
400
+ );
401
+
402
+ expect(projectJson.targets.serve.options.command).toContain("uvicorn");
403
+ expect(projectJson.targets.test.options.command).toContain("pytest");
404
+ });
405
+
406
+ test("generates project.json for React frontend", async () => {
407
+ const projectDir = join(tempDir, "react-frontend");
408
+ await mkdir(projectDir, { recursive: true });
409
+ await mkdir(join(projectDir, "backend"), { recursive: true });
410
+ await mkdir(join(projectDir, "frontend"), { recursive: true });
411
+
412
+ const context = {
413
+ name: "nx-react-app",
414
+ projectDir,
415
+ backendDir: "backend",
416
+ frontendDir: "frontend",
417
+ backend: "node-hono",
418
+ frontend: "react-vite",
419
+ };
420
+
421
+ await generateModule("build", "nx", projectDir, context);
422
+
423
+ const projectJson = JSON.parse(
424
+ await readFile(join(projectDir, "frontend/project.json"), "utf-8"),
425
+ );
426
+
427
+ expect(projectJson.name).toBe("nx-react-app-frontend");
428
+ expect(projectJson.targets.build.executor).toContain("vite");
429
+ expect(projectJson.targets.serve.executor).toContain("vite");
430
+ expect(projectJson.implicitDependencies).toContain("nx-react-app-backend");
431
+ });
432
+
433
+ test("nx.json plugins vary by backend language", async () => {
434
+ // Test that different backends get appropriate Nx plugins
435
+ const backends = [
436
+ { backend: "node-hono", expected: "@nx/js/typescript" },
437
+ { backend: "go-chi", expected: "@nx-go/nx-go" },
438
+ { backend: "python-fastapi", expected: "@nx/python" },
439
+ { backend: "rust-axum", expected: "@monodon/rust" },
440
+ ];
441
+
442
+ for (const { backend, expected } of backends) {
443
+ const projectDir = join(tempDir, `nx-${backend}`);
444
+ await mkdir(projectDir, { recursive: true });
445
+ await mkdir(join(projectDir, "backend"), { recursive: true });
446
+
447
+ const context = {
448
+ name: `nx-${backend}-app`,
449
+ projectDir,
450
+ backendDir: "backend",
451
+ backend,
452
+ frontend: "none",
453
+ };
454
+
455
+ await generateModule("build", "nx", projectDir, context);
456
+
457
+ const nxJson = JSON.parse(await readFile(join(projectDir, "nx.json"), "utf-8"));
458
+ const plugins = JSON.stringify(nxJson.plugins);
459
+ expect(plugins).toContain(expected);
460
+ }
461
+ });
462
+ });
463
+
464
+ describe("Turborepo Module", () => {
465
+ let tempDir: string;
466
+
467
+ beforeAll(async () => {
468
+ tempDir = await mkdtemp(join(tmpdir(), "arbiter-turbo-"));
469
+ });
470
+
471
+ afterAll(async () => {
472
+ await rm(tempDir, { recursive: true, force: true });
473
+ });
474
+
475
+ test("generates repo-level turbo.json", async () => {
476
+ const projectDir = join(tempDir, "repo-level");
477
+ await mkdir(projectDir, { recursive: true });
478
+
479
+ const context = {
480
+ name: "turbo-app",
481
+ projectDir,
482
+ backend: "none",
483
+ frontend: "none",
484
+ };
485
+
486
+ await generateModule("build", "turborepo", projectDir, context);
487
+
488
+ const files = await readdir(projectDir);
489
+ expect(files).toContain("turbo.json");
490
+
491
+ const turboJson = JSON.parse(await readFile(join(projectDir, "turbo.json"), "utf-8"));
492
+ expect(turboJson.$schema).toBeDefined();
493
+ expect(turboJson.tasks).toBeDefined();
494
+ expect(turboJson.tasks.build).toBeDefined();
495
+ expect(turboJson.tasks.dev).toBeDefined();
496
+ expect(turboJson.tasks.test).toBeDefined();
497
+ expect(turboJson.tasks.lint).toBeDefined();
498
+ });
499
+
500
+ test("generates package-level turbo.json for Node.js backend", async () => {
501
+ const projectDir = join(tempDir, "node-backend");
502
+ await mkdir(projectDir, { recursive: true });
503
+ await mkdir(join(projectDir, "backend"), { recursive: true });
504
+
505
+ const context = {
506
+ name: "turbo-node-app",
507
+ projectDir,
508
+ backendDir: "backend",
509
+ backend: "node-hono",
510
+ frontend: "none",
511
+ };
512
+
513
+ await generateModule("build", "turborepo", projectDir, context);
514
+
515
+ const backendTurbo = JSON.parse(
516
+ await readFile(join(projectDir, "backend/turbo.json"), "utf-8"),
517
+ );
518
+
519
+ expect(backendTurbo.extends).toContain("//");
520
+ expect(backendTurbo.tasks.build).toBeDefined();
521
+ expect(backendTurbo.tasks.build.outputs).toContain("dist/**");
522
+ });
523
+
524
+ test("generates package-level turbo.json for Go backend", async () => {
525
+ const projectDir = join(tempDir, "go-backend");
526
+ await mkdir(projectDir, { recursive: true });
527
+ await mkdir(join(projectDir, "backend"), { recursive: true });
528
+
529
+ const context = {
530
+ name: "turbo-go-app",
531
+ projectDir,
532
+ backendDir: "backend",
533
+ backend: "go-chi",
534
+ frontend: "none",
535
+ };
536
+
537
+ await generateModule("build", "turborepo", projectDir, context);
538
+
539
+ const backendTurbo = JSON.parse(
540
+ await readFile(join(projectDir, "backend/turbo.json"), "utf-8"),
541
+ );
542
+
543
+ expect(backendTurbo.tasks.build.outputs).toContain("bin/**");
544
+ expect(backendTurbo.tasks.test.inputs).toContain("**/*.go");
545
+ });
546
+
547
+ test("generates package-level turbo.json for Python backend", async () => {
548
+ const projectDir = join(tempDir, "python-backend");
549
+ await mkdir(projectDir, { recursive: true });
550
+ await mkdir(join(projectDir, "backend"), { recursive: true });
551
+
552
+ const context = {
553
+ name: "turbo-python-app",
554
+ projectDir,
555
+ backendDir: "backend",
556
+ backend: "python-fastapi",
557
+ frontend: "none",
558
+ };
559
+
560
+ await generateModule("build", "turborepo", projectDir, context);
561
+
562
+ const backendTurbo = JSON.parse(
563
+ await readFile(join(projectDir, "backend/turbo.json"), "utf-8"),
564
+ );
565
+
566
+ expect(backendTurbo.tasks.test.inputs).toContain("app/**/*.py");
567
+ expect(backendTurbo.tasks.lint.inputs).toContain("ruff.toml");
568
+ });
569
+
570
+ test("generates package-level turbo.json for Rust backend", async () => {
571
+ const projectDir = join(tempDir, "rust-backend");
572
+ await mkdir(projectDir, { recursive: true });
573
+ await mkdir(join(projectDir, "backend"), { recursive: true });
574
+
575
+ const context = {
576
+ name: "turbo-rust-app",
577
+ projectDir,
578
+ backendDir: "backend",
579
+ backend: "rust-axum",
580
+ frontend: "none",
581
+ };
582
+
583
+ await generateModule("build", "turborepo", projectDir, context);
584
+
585
+ const backendTurbo = JSON.parse(
586
+ await readFile(join(projectDir, "backend/turbo.json"), "utf-8"),
587
+ );
588
+
589
+ expect(backendTurbo.tasks.build.outputs).toContain("target/release/**");
590
+ expect(backendTurbo.tasks.test.inputs).toContain("src/**/*.rs");
591
+ });
592
+
593
+ test("generates frontend turbo.json for React", async () => {
594
+ const projectDir = join(tempDir, "react-frontend");
595
+ await mkdir(projectDir, { recursive: true });
596
+ await mkdir(join(projectDir, "frontend"), { recursive: true });
597
+
598
+ const context = {
599
+ name: "turbo-react-app",
600
+ projectDir,
601
+ frontendDir: "frontend",
602
+ backend: "none",
603
+ frontend: "react-vite",
604
+ };
605
+
606
+ await generateModule("build", "turborepo", projectDir, context);
607
+
608
+ const frontendTurbo = JSON.parse(
609
+ await readFile(join(projectDir, "frontend/turbo.json"), "utf-8"),
610
+ );
611
+
612
+ expect(frontendTurbo.tasks.build.env).toContain("VITE_API_URL");
613
+ expect(frontendTurbo.tasks.preview).toBeDefined();
614
+ });
615
+
616
+ test("full-stack generates both backend and frontend configs", async () => {
617
+ const projectDir = join(tempDir, "full-stack");
618
+ await mkdir(projectDir, { recursive: true });
619
+ await mkdir(join(projectDir, "backend"), { recursive: true });
620
+ await mkdir(join(projectDir, "frontend"), { recursive: true });
621
+
622
+ const context = {
623
+ name: "turbo-full-stack",
624
+ projectDir,
625
+ backendDir: "backend",
626
+ frontendDir: "frontend",
627
+ backend: "node-hono",
628
+ frontend: "react-vite",
629
+ };
630
+
631
+ await generateModule("build", "turborepo", projectDir, context);
632
+
633
+ // Verify all files exist
634
+ expect(await Bun.file(join(projectDir, "turbo.json")).exists()).toBe(true);
635
+ expect(await Bun.file(join(projectDir, "backend/turbo.json")).exists()).toBe(true);
636
+ expect(await Bun.file(join(projectDir, "frontend/turbo.json")).exists()).toBe(true);
637
+ });
638
+
639
+ test(
640
+ "turbo validates generated config",
641
+ async () => {
642
+ if (!tools.turbo) {
643
+ console.log("Skipping: turbo not available");
644
+ return;
645
+ }
646
+
647
+ const projectDir = join(tempDir, "turbo-validate");
648
+ await mkdir(projectDir, { recursive: true });
649
+
650
+ const context = {
651
+ name: "turbo-validate",
652
+ projectDir,
653
+ backend: "none",
654
+ frontend: "none",
655
+ };
656
+
657
+ await generateModule("build", "turborepo", projectDir, context);
658
+
659
+ // Create package.json for turbo
660
+ await Bun.write(
661
+ join(projectDir, "package.json"),
662
+ JSON.stringify({ name: "turbo-validate", private: true, workspaces: [] }, null, 2),
663
+ );
664
+
665
+ // turbo should be able to parse the config
666
+ const result = await $`cd ${projectDir} && turbo build --dry-run=json 2>&1`.nothrow();
667
+ expect(result.exitCode).toBeLessThanOrEqual(1);
668
+ },
669
+ TEST_TIMEOUT,
670
+ );
671
+ });
672
+
673
+ describe("Build System Integration", () => {
674
+ let tempDir: string;
675
+
676
+ beforeAll(async () => {
677
+ tempDir = await mkdtemp(join(tmpdir(), "arbiter-build-int-"));
678
+ });
679
+
680
+ afterAll(async () => {
681
+ await rm(tempDir, { recursive: true, force: true });
682
+ });
683
+
684
+ test("multiple build systems can be generated together", async () => {
685
+ // While not recommended in practice, the modules should not conflict
686
+ const projectDir = join(tempDir, "multi-build");
687
+ await mkdir(projectDir, { recursive: true });
688
+ await mkdir(join(projectDir, "backend"), { recursive: true });
689
+
690
+ const context = {
691
+ name: "multi-build-app",
692
+ projectDir,
693
+ backendDir: "backend",
694
+ backend: "node-hono",
695
+ frontend: "none",
696
+ };
697
+
698
+ // Generate all three build systems
699
+ await generateModule("build", "bazel", projectDir, context);
700
+ await generateModule("build", "nx", projectDir, context);
701
+ await generateModule("build", "turborepo", projectDir, context);
702
+
703
+ // All should coexist
704
+ const files = await readdir(projectDir);
705
+ expect(files).toContain("WORKSPACE.bazel");
706
+ expect(files).toContain("nx.json");
707
+ expect(files).toContain("turbo.json");
708
+
709
+ const backendFiles = await readdir(join(projectDir, "backend"));
710
+ expect(backendFiles).toContain("BUILD.bazel");
711
+ expect(backendFiles).toContain("project.json");
712
+ expect(backendFiles).toContain("turbo.json");
713
+ });
714
+
715
+ test("generated scripts are consistent across build systems", async () => {
716
+ const bazelMod = await loadModule("build", "bazel");
717
+ const nxMod = await loadModule("build", "nx");
718
+ const turboMod = await loadModule("build", "turborepo");
719
+
720
+ // All should have build, test scripts
721
+ expect(bazelMod.scripts).toHaveProperty("bazel:build");
722
+ expect(bazelMod.scripts).toHaveProperty("bazel:test");
723
+
724
+ expect(nxMod.scripts).toHaveProperty("nx:build");
725
+ expect(nxMod.scripts).toHaveProperty("nx:test");
726
+
727
+ expect(turboMod.scripts).toHaveProperty("build");
728
+ expect(turboMod.scripts).toHaveProperty("test");
729
+ });
730
+ });