From 7b4c00a2a0a97f2c991f3c0a56ab1ba297df11bd Mon Sep 17 00:00:00 2001 From: Cuzeac Florin Date: Sat, 6 Jun 2026 16:22:02 +0200 Subject: [PATCH] fix(cli): adapt to shadcn template drift breaking start and next scaffolds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TanStack Start scaffolds aborted with "Could not mount ExternalLinkGuard in the TanStack Start root route." The upstream template now ships a notFoundComponent with its own
before RootDocument, and the scroll-container battery's "upgrade an existing
" regex matched the first
in the file — tagging the 404 page's element instead of wrapping {children}, which left nothing for the external-link-guard battery to mount into. The upgrade path now only targets a
that directly wraps the route content ({children}, , , ) across all five templates. Next.js scaffolds aborted with ENOENT on next.config.mjs. The template now generates a typed next.config.ts and a plain "next dev" script without --turbopack. The adapter now resolves next.config.{ts,mjs,js}, patches the typed config shape, and appends -p 1420 to any "next dev" script that doesn't already pin a port. Verified with the simple test matrix: all five templates scaffold and build cleanly. Co-Authored-By: Claude Opus 4.8 (1M context) --- .changeset/clever-moons-dance.md | 8 +++++ packages/create-tauri-ui/src/adapters/next.ts | 32 ++++++++++++------- .../src/batteries/scroll-container.ts | 19 +++++++++-- 3 files changed, 45 insertions(+), 14 deletions(-) create mode 100644 .changeset/clever-moons-dance.md diff --git a/.changeset/clever-moons-dance.md b/.changeset/clever-moons-dance.md new file mode 100644 index 0000000..b99893f --- /dev/null +++ b/.changeset/clever-moons-dance.md @@ -0,0 +1,8 @@ +--- +"create-tauri-ui": patch +--- + +Fix scaffolding failures caused by upstream template drift in the shadcn templates. + +- **TanStack Start**: scaffolding aborted with "Could not mount ExternalLinkGuard in the TanStack Start root route." The template now ships a `notFoundComponent` containing its own `
` element before `RootDocument`, and the scroll-container battery's "upgrade an existing `
`" path matched the first `
` in the file — tagging the 404 page's `
` with `data-ui-scroll-container` instead of wrapping `{children}`. The external-link-guard battery then failed because `
{children}
` never existed. The upgrade path now only targets a `
` that directly wraps the route content (`{children}`, ``, ``, or ``) across all five templates. +- **Next.js**: scaffolding aborted with `ENOENT ... next.config.mjs`. The template now generates a typed `next.config.ts` (`const nextConfig: NextConfig = {}`) and a plain `next dev` script without `--turbopack`. The adapter now resolves `next.config.ts`/`.mjs`/`.js`, patches the typed config shape, and appends `-p 1420` to any `next dev` script that doesn't already pin a port. diff --git a/packages/create-tauri-ui/src/adapters/next.ts b/packages/create-tauri-ui/src/adapters/next.ts index c5bb2ce..bc9d1eb 100644 --- a/packages/create-tauri-ui/src/adapters/next.ts +++ b/packages/create-tauri-ui/src/adapters/next.ts @@ -1,37 +1,47 @@ +import fs from "node:fs"; import path from "node:path"; import type { ProjectOptions, TemplateAdapter } from "../types"; import { PatchError, editFile, editJson } from "../utils"; +const NEXT_CONFIG_FILES = ["next.config.ts", "next.config.mjs", "next.config.js"]; + export const nextAdapter: TemplateAdapter = { name: "next", async apply(projectDir: string, _options: ProjectOptions) { - editFile(path.join(projectDir, "next.config.mjs"), (content) => { + const configFile = NEXT_CONFIG_FILES.find((file) => fs.existsSync(path.join(projectDir, file))); + + if (!configFile) { + throw new PatchError("next.config.ts", "Could not find the Next.js config file."); + } + + editFile(path.join(projectDir, configFile), (content) => { if (content.includes('output: "export"')) { return content; } - if (!content.includes("const nextConfig = {}")) { - throw new PatchError("next.config.mjs", "Could not find the default Next.js config shape."); + const emptyConfig = /const nextConfig(?:\s*:\s*NextConfig)?\s*=\s*\{\}/; + + if (!emptyConfig.test(content)) { + throw new PatchError(configFile, "Could not find the default Next.js config shape."); } - return content.replace( - "const nextConfig = {}", - `const nextConfig = { + return content.replace(emptyConfig, (declaration) => + declaration.replace( + "{}", + `{ output: "export", images: { unoptimized: true, }, }`, + ), ); }); editJson>(path.join(projectDir, "package.json"), (pkg) => { - if (pkg.scripts?.dev) { - pkg.scripts.dev = pkg.scripts.dev.replace( - "next dev --turbopack", - "next dev --turbopack -p 1420", - ); + if (pkg.scripts?.dev?.startsWith("next dev") && !pkg.scripts.dev.includes("-p ")) { + pkg.scripts.dev = `${pkg.scripts.dev} -p 1420`; } return pkg; diff --git a/packages/create-tauri-ui/src/batteries/scroll-container.ts b/packages/create-tauri-ui/src/batteries/scroll-container.ts index 2425b2c..df7aaab 100644 --- a/packages/create-tauri-ui/src/batteries/scroll-container.ts +++ b/packages/create-tauri-ui/src/batteries/scroll-container.ts @@ -43,14 +43,22 @@ function ensureCssSnippet(filePath: string, snippet: string) { }); } -function ensureMainWrapper(filePath: string, matcher: string | RegExp, replacement: string) { +function ensureMainWrapper( + filePath: string, + innerAnchor: string, + matcher: string | RegExp, + replacement: string, +) { editFile(filePath, (content) => { - if (content.includes(`
`)) { + if (content.includes(SCROLL_CONTAINER_ATTRIBUTE)) { return content; } + // Only upgrade a
that directly wraps the route content. Templates can + // contain unrelated
elements (e.g. the TanStack Start notFoundComponent), + // which must not become the desktop scroll container. const upgradedContent = content.replace( - new RegExp(`]*${SCROLL_CONTAINER_ATTRIBUTE})([^>]*)>`), + new RegExp(`]*${SCROLL_CONTAINER_ATTRIBUTE})([^>]*)>(?=\\s*${innerAnchor})`), `
`, ); @@ -74,6 +82,7 @@ export async function applyScrollContainer(projectDir: string, options: ProjectO ensureCssSnippet(path.join(projectDir, "app/globals.css"), BASE_SCROLL_CSS); ensureMainWrapper( path.join(projectDir, "app/layout.tsx"), + "\\{children\\}", /\{children\}/, `
{children}
`, ); @@ -85,6 +94,7 @@ export async function applyScrollContainer(projectDir: string, options: ProjectO ); ensureMainWrapper( path.join(projectDir, "src/main.tsx"), + "", //, `
`, ); @@ -93,6 +103,7 @@ export async function applyScrollContainer(projectDir: string, options: ProjectO ensureCssSnippet(path.join(projectDir, "src/styles.css"), BASE_SCROLL_CSS); ensureMainWrapper( path.join(projectDir, "src/routes/__root.tsx"), + "\\{children\\}", /\{children\}/, `
{children}
`, ); @@ -101,6 +112,7 @@ export async function applyScrollContainer(projectDir: string, options: ProjectO ensureCssSnippet(path.join(projectDir, "app/app.css"), BASE_SCROLL_CSS); ensureMainWrapper( path.join(projectDir, "app/root.tsx"), + "", /return /, `return
`, ); @@ -109,6 +121,7 @@ export async function applyScrollContainer(projectDir: string, options: ProjectO ensureCssSnippet(path.join(projectDir, "src/styles/global.css"), BASE_SCROLL_CSS); ensureMainWrapper( path.join(projectDir, "src/layouts/main.astro"), + "", //, `
`, );