Make generated Vue monorepos buildable with docker compose

Running `draft new monorepo` with the Vue client and then
`docker compose build client` failed three separate ways, each found by
actually doing it:

- No Dockerfile: the Vue overlay never shipped one. The template now has
  an nginx-based image (project-template#4) and the client-vue sync job
  embeds it plus its nginx.conf.template, with the pnpm pin rewritten to
  the local version like the Nuxt overlay.
- Stale root lockfile: the Vue scaffold wrote a pnpm-workspace.yaml into
  apps/client (to approve vue-demi's build script), which made it its own
  workspace, so the installs updated a nested lockfile and the root one
  kept describing the template's Nuxt client - pnpm install
  --frozen-lockfile then refused it. The file is only written when the
  client isn't already inside a workspace (standalone `draft new client`).
- Missing component: the client-vue job listed components by name, so the
  AuthCard.vue that Login/Signup import was never embedded. It now embeds
  src/components/ as a whole.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
FernandoJVideiraandClaude Fable 5.1 committed 2026-09-16 13:22:37 +01:00
1 parent 1195fdacb5
commit 230037c5f5
4 files changed
+34 -10

No files matched your search

+29 -6
View File
@@ -153,6 +153,19 @@ func patchNuxtConfig(projectDir, serviceName string) error {
return os.WriteFile(configPath, []byte(finalContent), 0644)
}
func insidePnpmWorkspace(dir string) bool {
for {
if _, err := os.Stat(filepath.Join(dir, "pnpm-workspace.yaml")); err == nil {
return true
}
parent := filepath.Dir(dir)
if parent == dir {
return false
}
dir = parent
}
}
func scaffoldClientVue(projectDir, serviceName string) error {
parentDir := filepath.Dir(projectDir)
leafName := filepath.Base(projectDir)
@@ -173,12 +186,19 @@ func scaffoldClientVue(projectDir, serviceName string) error {
return err
}
if err := os.WriteFile(
filepath.Join(projectDir, "pnpm-workspace.yaml"),
[]byte("allowBuilds:\n vue-demi: true\n"),
0644,
); err != nil {
return fmt.Errorf("error writing pnpm-workspace.yaml: %w", err)
// A standalone client is its own workspace root and needs vue-demi's build
// script approved. Inside a monorepo the root pnpm-workspace.yaml already
// does that - and writing one here would make apps/client a separate
// workspace, so the installs below would update a nested lockfile and
// leave the root one describing the template's client instead of this one.
if !insidePnpmWorkspace(parentDir) {
if err := os.WriteFile(
filepath.Join(projectDir, "pnpm-workspace.yaml"),
[]byte("allowBuilds:\n vue-demi: true\n"),
0644,
); err != nil {
return fmt.Errorf("error writing pnpm-workspace.yaml: %w", err)
}
}
fmt.Println("📦 Installing base dependencies...")
@@ -210,6 +230,9 @@ func scaffoldClientVue(projectDir, serviceName string) error {
replacements := map[string]string{
"App Template": serviceName,
// The overlay's Dockerfile pins the pnpm that produced the template's
// lockfile; the generated project's lockfile comes from the local pnpm.
"[email protected]": "pnpm@" + version,
}
if err := copyEmbeddedDir("embedded/client-vue", projectDir, replacements); err != nil {
return fmt.Errorf("error copying client overlay: %w", err)