Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/clever-moons-dance.md
Original file line number Diff line number Diff line change
@@ -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 `<main>` element before `RootDocument`, and the scroll-container battery's "upgrade an existing `<main>`" path matched the first `<main>` in the file — tagging the 404 page's `<main>` with `data-ui-scroll-container` instead of wrapping `{children}`. The external-link-guard battery then failed because `<main>{children}</main>` never existed. The upgrade path now only targets a `<main>` that directly wraps the route content (`{children}`, `<App />`, `<Outlet />`, or `<slot />`) 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.
32 changes: 21 additions & 11 deletions packages/create-tauri-ui/src/adapters/next.ts
Original file line number Diff line number Diff line change
@@ -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<Record<string, any>>(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;
Expand Down
19 changes: 16 additions & 3 deletions packages/create-tauri-ui/src/batteries/scroll-container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(`<main ${SCROLL_CONTAINER_ATTRIBUTE}>`)) {
if (content.includes(SCROLL_CONTAINER_ATTRIBUTE)) {
return content;
}

// Only upgrade a <main> that directly wraps the route content. Templates can
// contain unrelated <main> elements (e.g. the TanStack Start notFoundComponent),
// which must not become the desktop scroll container.
const upgradedContent = content.replace(
new RegExp(`<main(?![^>]*${SCROLL_CONTAINER_ATTRIBUTE})([^>]*)>`),
new RegExp(`<main(?![^>]*${SCROLL_CONTAINER_ATTRIBUTE})([^>]*)>(?=\\s*${innerAnchor})`),
`<main ${SCROLL_CONTAINER_ATTRIBUTE}$1>`,
);

Expand All @@ -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\}/,
`<main ${SCROLL_CONTAINER_ATTRIBUTE}>{children}</main>`,
);
Expand All @@ -85,6 +94,7 @@ export async function applyScrollContainer(projectDir: string, options: ProjectO
);
ensureMainWrapper(
path.join(projectDir, "src/main.tsx"),
"<App \\/>",
/<App \/>/,
`<main ${SCROLL_CONTAINER_ATTRIBUTE}><App /></main>`,
);
Expand All @@ -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\}/,
`<main ${SCROLL_CONTAINER_ATTRIBUTE}>{children}</main>`,
);
Expand All @@ -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"),
"<Outlet \\/>",
/return <Outlet \/>/,
`return <main ${SCROLL_CONTAINER_ATTRIBUTE}><Outlet /></main>`,
);
Expand All @@ -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"),
"<slot \\/>",
/<slot \/>/,
`<main ${SCROLL_CONTAINER_ATTRIBUTE}><slot /></main>`,
);
Expand Down