Public Access
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:
1 parent
1195fdacb5
commit
230037c5f5
4 files changed
+28
-4
No files matched your search
@@ -85,7 +85,7 @@ draft new client
|
|||||||
3. Scaffolds the base app via the official tooling (`nuxi init` for Nuxt, `create-vue` for Vue), so the generated boilerplate, dependency versions, and lockfile are always current rather than pinned by Draft
|
3. Scaffolds the base app via the official tooling (`nuxi init` for Nuxt, `create-vue` for Vue), so the generated boilerplate, dependency versions, and lockfile are always current rather than pinned by Draft
|
||||||
4. Layers a Draft-maintained overlay on top: an auth store (login/logout/register/token refresh), route guards, API composable, and pages for login/signup/profile/organizations/email verification/invites — all wired up to talk to a Draft-generated API out of the box
|
4. Layers a Draft-maintained overlay on top: an auth store (login/logout/register/token refresh), route guards, API composable, and pages for login/signup/profile/organizations/email verification/invites — all wired up to talk to a Draft-generated API out of the box
|
||||||
|
|
||||||
Both frameworks produce a comparable app: [`vue-ui-kit`](https://git.fernandovideira.com/FernandoJVideira/vue-ui-kit) (installed from its own Gitea npm registry - see its README for the one-time `.npmrc` token setup) + Tailwind, the same component library and theme tokens on either framework. The generated Nuxt client's Dockerfile mounts that `~/.npmrc` as a build secret (wired up in the monorepo's `docker-compose.yml`), so container builds resolve the package without baking the token into the image.
|
Both frameworks produce a comparable app: [`vue-ui-kit`](https://git.fernandovideira.com/FernandoJVideira/vue-ui-kit) (installed from its own Gitea npm registry - see its README for the one-time `.npmrc` token setup) + Tailwind, the same component library and theme tokens on either framework. Both clients ship with a Dockerfile that mounts that `~/.npmrc` as a build secret (wired up in the monorepo's `docker-compose.yml`), so container builds resolve the package without baking the token into the image - Nuxt runs its Nitro server, Vue is served by nginx with an SPA fallback and `/api/` proxied to the api container.
|
||||||
|
|
||||||
### Create a New Monorepo Project
|
### Create a New Monorepo Project
|
||||||
|
|
||||||
|
|||||||
Vendored
+1
-1
Submodule assets/testdata/template updated: 70067c968a...4143dea0b3.
@@ -153,6 +153,19 @@ func patchNuxtConfig(projectDir, serviceName string) error {
|
|||||||
return os.WriteFile(configPath, []byte(finalContent), 0644)
|
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 {
|
func scaffoldClientVue(projectDir, serviceName string) error {
|
||||||
parentDir := filepath.Dir(projectDir)
|
parentDir := filepath.Dir(projectDir)
|
||||||
leafName := filepath.Base(projectDir)
|
leafName := filepath.Base(projectDir)
|
||||||
@@ -173,6 +186,12 @@ func scaffoldClientVue(projectDir, serviceName string) error {
|
|||||||
return err
|
return 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(
|
if err := os.WriteFile(
|
||||||
filepath.Join(projectDir, "pnpm-workspace.yaml"),
|
filepath.Join(projectDir, "pnpm-workspace.yaml"),
|
||||||
[]byte("allowBuilds:\n vue-demi: true\n"),
|
[]byte("allowBuilds:\n vue-demi: true\n"),
|
||||||
@@ -180,6 +199,7 @@ func scaffoldClientVue(projectDir, serviceName string) error {
|
|||||||
); err != nil {
|
); err != nil {
|
||||||
return fmt.Errorf("error writing pnpm-workspace.yaml: %w", err)
|
return fmt.Errorf("error writing pnpm-workspace.yaml: %w", err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Println("📦 Installing base dependencies...")
|
fmt.Println("📦 Installing base dependencies...")
|
||||||
if err := runCommand(projectDir, "pnpm", "install"); err != nil {
|
if err := runCommand(projectDir, "pnpm", "install"); err != nil {
|
||||||
@@ -210,6 +230,9 @@ func scaffoldClientVue(projectDir, serviceName string) error {
|
|||||||
|
|
||||||
replacements := map[string]string{
|
replacements := map[string]string{
|
||||||
"App Template": serviceName,
|
"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 {
|
if err := copyEmbeddedDir("embedded/client-vue", projectDir, replacements); err != nil {
|
||||||
return fmt.Errorf("error copying client overlay: %w", err)
|
return fmt.Errorf("error copying client overlay: %w", err)
|
||||||
|
|||||||
@@ -52,11 +52,12 @@ var jobs = []syncJob{
|
|||||||
"src/stores/",
|
"src/stores/",
|
||||||
"src/composables/",
|
"src/composables/",
|
||||||
"src/lib/api-client.ts",
|
"src/lib/api-client.ts",
|
||||||
"src/components/AppBrand.vue",
|
"src/components/",
|
||||||
"src/components/AppLayout.vue",
|
|
||||||
"src/views/",
|
"src/views/",
|
||||||
"src/shared/",
|
"src/shared/",
|
||||||
".env.example",
|
".env.example",
|
||||||
|
"Dockerfile",
|
||||||
|
"nginx.conf.template",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user