From 810720bb3e18f2d3d214b621dcc1fb0ec434222a Mon Sep 17 00:00:00 2001 From: Abdullahi Mohamed <126521894+WaryaWayne@users.noreply.github.com> Date: Tue, 14 Apr 2026 18:37:44 -0400 Subject: [PATCH 1/5] fix(create): exclude demo files in lib, hooks, data, and components when demo is disabled Previously only demo route files were filtered out when the user chose no demo files. Demo files in components/, lib/, hooks/, and data/ directories would still be created. Rename isDemoRoutePath to isDemoFilePath to reflect the broader scope and extend pattern matching to cover all non-route demo file paths. --- packages/create/src/create-app.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/create/src/create-app.ts b/packages/create/src/create-app.ts index eaa93e0f..f4f77a7a 100644 --- a/packages/create/src/create-app.ts +++ b/packages/create/src/create-app.ts @@ -17,14 +17,22 @@ import { runSpecialSteps } from './special-steps/index.js' import type { Environment, FileBundleHandler, Options } from './types.js' -function isDemoRoutePath(path?: string) { +function isDemoFilePath(path?: string) { if (!path) return false const normalized = path.replace(/\\/g, '/') return ( normalized.includes('/routes/demo/') || normalized.includes('/routes/demo.') || normalized.includes('/routes/example/') || - normalized.includes('/routes/example.') + normalized.includes('/routes/example.') || + normalized.includes('/lib/demo-') || + normalized.includes('/lib/demo.') || + normalized.includes('/hooks/demo-') || + normalized.includes('/hooks/demo.') || + normalized.includes('/data/demo.') || + normalized.includes('/data/demo-') || + normalized.includes('/components/demo-') || + normalized.includes('/components/demo.') ) } @@ -38,7 +46,7 @@ function stripExamplesFromOptions(options: Options): Options { .map((addOn) => { const filteredRoutes = (addOn.routes || []).filter( (route) => - !isDemoRoutePath(route.path) && + !isDemoFilePath(route.path) && !(route.url && route.url.startsWith('/demo')), ) @@ -47,11 +55,11 @@ function stripExamplesFromOptions(options: Options): Options { routes: filteredRoutes, getFiles: async () => { const files = await addOn.getFiles() - return files.filter((file) => !isDemoRoutePath(file)) + return files.filter((file) => !isDemoFilePath(file)) }, getDeletedFiles: async () => { const deletedFiles = await addOn.getDeletedFiles() - return deletedFiles.filter((file) => !isDemoRoutePath(file)) + return deletedFiles.filter((file) => !isDemoFilePath(file)) }, } }) From 95707586f44371f94a98715bebe7368a8d6e34cf Mon Sep 17 00:00:00 2001 From: Abdullahi Mohamed <126521894+WaryaWayne@users.noreply.github.com> Date: Wed, 15 Apr 2026 10:10:19 -0400 Subject: [PATCH 2/5] fix(create): exclude better-auth demo integration files when demo is disabled The better-auth header-user components reference the /demo/better-auth route, which doesn't exist when users opt out of demo files. Convert these to EJS templates with conditional rendering so they return null instead of linking to a non-existent route. Also extend isDemoFilePath filtering to cover add-on integration files (previously only routes, lib, hooks, data, and components were filtered in a037c4d5). --- packages/create/src/create-app.ts | 7 ++++++- .../better-auth/{header-user.tsx => header-user.tsx.ejs} | 6 ++++++ .../better-auth/{header-user.tsx => header-user.tsx.ejs} | 4 ++++ 3 files changed, 16 insertions(+), 1 deletion(-) rename packages/create/src/frameworks/react/add-ons/better-auth/assets/src/integrations/better-auth/{header-user.tsx => header-user.tsx.ejs} (89%) rename packages/create/src/frameworks/solid/add-ons/better-auth/assets/src/integrations/better-auth/{header-user.tsx => header-user.tsx.ejs} (92%) diff --git a/packages/create/src/create-app.ts b/packages/create/src/create-app.ts index f4f77a7a..8d978c9e 100644 --- a/packages/create/src/create-app.ts +++ b/packages/create/src/create-app.ts @@ -29,8 +29,8 @@ function isDemoFilePath(path?: string) { normalized.includes('/lib/demo.') || normalized.includes('/hooks/demo-') || normalized.includes('/hooks/demo.') || - normalized.includes('/data/demo.') || normalized.includes('/data/demo-') || + normalized.includes('/data/demo.') || normalized.includes('/components/demo-') || normalized.includes('/components/demo.') ) @@ -49,10 +49,15 @@ function stripExamplesFromOptions(options: Options): Options { !isDemoFilePath(route.path) && !(route.url && route.url.startsWith('/demo')), ) + + const filteredIntegrations = (addOn.integrations || []).filter( + (integration) => !isDemoFilePath(integration.path) + ) return { ...addOn, routes: filteredRoutes, + integrations: filteredIntegrations, getFiles: async () => { const files = await addOn.getFiles() return files.filter((file) => !isDemoFilePath(file)) diff --git a/packages/create/src/frameworks/react/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx b/packages/create/src/frameworks/react/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx.ejs similarity index 89% rename from packages/create/src/frameworks/react/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx rename to packages/create/src/frameworks/react/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx.ejs index 6f07c17b..2fd3dca1 100644 --- a/packages/create/src/frameworks/react/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx +++ b/packages/create/src/frameworks/react/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx.ejs @@ -1,5 +1,7 @@ import { authClient } from "#/lib/auth-client"; +<%_ if (routes.some(r => r.url === '/demo/better-auth')) { _%> import { Link } from "@tanstack/react-router"; +<%_ } _%> export default function BetterAuthHeader() { const { data: session, isPending } = authClient.useSession(); @@ -34,6 +36,7 @@ export default function BetterAuthHeader() { ); } +<%_ if (routes.some(r => r.url === '/demo/better-auth')) { _%> return ( ); +<%_ } else { _%> + return null; +<%_ } _%> } diff --git a/packages/create/src/frameworks/solid/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx b/packages/create/src/frameworks/solid/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx.ejs similarity index 92% rename from packages/create/src/frameworks/solid/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx rename to packages/create/src/frameworks/solid/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx.ejs index a9aee45a..c560fcc6 100644 --- a/packages/create/src/frameworks/solid/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx +++ b/packages/create/src/frameworks/solid/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx.ejs @@ -1,5 +1,7 @@ import { Show } from "solid-js"; +<%_ if (routes.some(r => r.url === '/demo/better-auth')) { _%> import { Link } from "@tanstack/solid-router"; +<%_ } _%> import { authClient } from "../../lib/auth-client"; export default function BetterAuthHeader() { @@ -14,6 +16,7 @@ export default function BetterAuthHeader() { > r.url === '/demo/better-auth')) { _%> fallback={ } +<%_ } _%> > {(user) => (
From d530fa37e8b6b26b4f1801fcae31315beb464e6c Mon Sep 17 00:00:00 2001 From: wyMinLwin Date: Sun, 12 Apr 2026 00:23:52 +0700 Subject: [PATCH 3/5] fix(create): generate minimal scaffolding when declining demo/example pages --- .../base/src/components/Footer.tsx.ejs | 1 + .../base/src/components/Header.tsx.ejs | 1 + .../base/src/components/ThemeToggle.tsx.ejs | 1 + .../project/base/src/routes/__root.tsx.ejs | 93 +++++++++++++++++++ .../project/base/src/routes/about.tsx.ejs | 1 + .../project/base/src/routes/index.tsx.ejs | 17 ++++ .../react/project/base/src/styles.css.ejs | 18 ++++ .../base/src/components/Header.tsx.ejs | 1 + .../project/base/src/routes/__root.tsx.ejs | 45 +++++++++ .../project/base/src/routes/about.tsx.ejs | 1 + .../project/base/src/routes/index.tsx.ejs | 17 ++++ .../solid/project/base/src/styles.css.ejs | 18 ++++ packages/create/src/template-file.ts | 1 + .../create/tests/template-context.test.ts | 63 +++++++++++++ 14 files changed, 278 insertions(+) diff --git a/packages/create/src/frameworks/react/project/base/src/components/Footer.tsx.ejs b/packages/create/src/frameworks/react/project/base/src/components/Footer.tsx.ejs index 6156f908..f70e476b 100644 --- a/packages/create/src/frameworks/react/project/base/src/components/Footer.tsx.ejs +++ b/packages/create/src/frameworks/react/project/base/src/components/Footer.tsx.ejs @@ -1,3 +1,4 @@ +<% if (!includeExamples) { ignoreFile(); return; } %> export default function Footer() { const year = new Date().getFullYear() diff --git a/packages/create/src/frameworks/react/project/base/src/components/Header.tsx.ejs b/packages/create/src/frameworks/react/project/base/src/components/Header.tsx.ejs index f83a81c8..a6f3ed44 100644 --- a/packages/create/src/frameworks/react/project/base/src/components/Header.tsx.ejs +++ b/packages/create/src/frameworks/react/project/base/src/components/Header.tsx.ejs @@ -1,3 +1,4 @@ +<% if (!includeExamples) { ignoreFile(); return; } %> import { Link } from '@tanstack/react-router' <% for (const integration of integrations.filter((i) => i.type === 'header-user')) { %>import <%= integration.jsName %> from '<%= relativePath(integration.path) %>' <% } %>import ThemeToggle from './ThemeToggle' diff --git a/packages/create/src/frameworks/react/project/base/src/components/ThemeToggle.tsx.ejs b/packages/create/src/frameworks/react/project/base/src/components/ThemeToggle.tsx.ejs index 081ebe2b..61b6d82f 100644 --- a/packages/create/src/frameworks/react/project/base/src/components/ThemeToggle.tsx.ejs +++ b/packages/create/src/frameworks/react/project/base/src/components/ThemeToggle.tsx.ejs @@ -1,3 +1,4 @@ +<% if (!includeExamples) { ignoreFile(); return; } %> import { useEffect, useState } from 'react' type ThemeMode = 'light' | 'dark' | 'auto' diff --git a/packages/create/src/frameworks/react/project/base/src/routes/__root.tsx.ejs b/packages/create/src/frameworks/react/project/base/src/routes/__root.tsx.ejs index 22abf72c..2b3098ad 100644 --- a/packages/create/src/frameworks/react/project/base/src/routes/__root.tsx.ejs +++ b/packages/create/src/frameworks/react/project/base/src/routes/__root.tsx.ejs @@ -27,6 +27,99 @@ function RootComponent() { ) } +<% } else if (!includeExamples) { %> +<% let hasContext = addOnEnabled["apollo-client"] || addOnEnabled["tanstack-query"]; %> +import { + HeadContent, Scripts, <% if (hasContext) { %>createRootRouteWithContext<% } else { %>createRootRoute<% } %> } from '@tanstack/react-router' +import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools'; +import { TanStackDevtools } from '@tanstack/react-devtools' +<% for(const integration of integrations.filter(i => i.type === 'layout' || i.type === 'provider' || i.type === 'devtools')) { %> +import <%= integration.jsName %> from '<%= relativePath(integration.path, true) %>' +<% } %><% if (addOnEnabled.paraglide) { %> +import { getLocale } from '#/paraglide/runtime' +<% } %> +import appCss from '../styles.css?url' +<% if (addOnEnabled["apollo-client"]) { %> +import type { ApolloClientIntegration } from "@apollo/client-integration-tanstack-start"; +<% } %> +<% if (addOnEnabled["tanstack-query"]) { %> +import type { QueryClient } from '@tanstack/react-query' +<% if (addOnEnabled.tRPC) { %> +import type { TRPCRouter } from '#/integrations/trpc/router' +import type { TRPCOptionsProxy } from '@trpc/tanstack-react-query' +<% } %> +<% } %> +<% if (hasContext) { %> +interface MyRouterContext <% if (addOnEnabled["apollo-client"]) {%> extends ApolloClientIntegration.RouterContext <%} %>{ +<% if (addOnEnabled["tanstack-query"]) { %> + queryClient: QueryClient + <% if (addOnEnabled.tRPC) { %> + trpc: TRPCOptionsProxy + <% } %> +<% } %> +}<% } %> + +export const Route = <% if (hasContext) { %>createRootRouteWithContext()<% } else { %>createRootRoute<% } %>({ +<% if (addOnEnabled.paraglide) { %> + beforeLoad: async () => { + // Other redirect strategies are possible; see + // https://github.com/TanStack/router/tree/main/examples/react/i18n-paraglide#offline-redirect + if (typeof document !== 'undefined') { + document.documentElement.setAttribute('lang', getLocale()) + } + }, +<% } %> + head: () => ({ + meta: [ + { + charSet: 'utf-8', + }, + { + name: 'viewport', + content: 'width=device-width, initial-scale=1', + }, + { + title: 'TanStack Start Starter', + }, + ], + links: [ + { + rel: 'stylesheet', + href: appCss, + }, + ], + }), + shellComponent: RootDocument +}) + +function RootDocument({ children }: { children: React.ReactNode }) { + return ( + <% if (addOnEnabled.paraglide) { %><% } else { %><% } %> + + + + + <% for(const integration of integrations.filter(i => i.type === 'provider')) { %><<%= integration.jsName %>> + <% } %>{children} + , + }, + <% for(const integration of integrations.filter(i => i.type === 'devtools')) { %><%= integration.jsName %>,<% } %> + ]} + /> + <% for(const integration of integrations.filter(i => i.type === 'layout')) { %><<%= integration.jsName %> /> + <% } %><% for(const integration of integrations.filter(i => i.type === 'provider').reverse()) { %>> + <% } %> + + + ) +} <% } else { %> <% let hasContext = addOnEnabled["apollo-client"] || addOnEnabled["tanstack-query"]; %> import { diff --git a/packages/create/src/frameworks/react/project/base/src/routes/about.tsx.ejs b/packages/create/src/frameworks/react/project/base/src/routes/about.tsx.ejs index 68b82b55..ee76e3fe 100644 --- a/packages/create/src/frameworks/react/project/base/src/routes/about.tsx.ejs +++ b/packages/create/src/frameworks/react/project/base/src/routes/about.tsx.ejs @@ -1,3 +1,4 @@ +<% if (!includeExamples) { ignoreFile(); return; } %> import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/about')({ diff --git a/packages/create/src/frameworks/react/project/base/src/routes/index.tsx.ejs b/packages/create/src/frameworks/react/project/base/src/routes/index.tsx.ejs index 21754f74..7dd740be 100644 --- a/packages/create/src/frameworks/react/project/base/src/routes/index.tsx.ejs +++ b/packages/create/src/frameworks/react/project/base/src/routes/index.tsx.ejs @@ -1,3 +1,19 @@ +<% if (!includeExamples) { %> +import { createFileRoute } from "@tanstack/react-router"; + +export const Route = createFileRoute("/")({ component: Home }); + +function Home() { + return ( +
+

Welcome to TanStack Start

+

+ Edit src/routes/index.tsx to get started. +

+
+ ); +} +<% } else { %> import { createFileRoute } from "@tanstack/react-router"; export const Route = createFileRoute("/")({ component: App }); @@ -70,3 +86,4 @@ function App() { ); } +<% } %> diff --git a/packages/create/src/frameworks/react/project/base/src/styles.css.ejs b/packages/create/src/frameworks/react/project/base/src/styles.css.ejs index 498b4c53..559f0e80 100644 --- a/packages/create/src/frameworks/react/project/base/src/styles.css.ejs +++ b/packages/create/src/frameworks/react/project/base/src/styles.css.ejs @@ -1,3 +1,20 @@ +<% if (!includeExamples) { %> +@import "tailwindcss"; + +* { + box-sizing: border-box; +} + +html, +body, +#app { + min-height: 100%; +} + +body { + margin: 0; +} +<% } else { %> @import url("https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,700&family=Manrope:wght@400;500;600;700;800&display=swap"); @import "tailwindcss"; @plugin "@tailwindcss/typography"; @@ -257,3 +274,4 @@ a { transform: translateY(0); } } +<% } %> diff --git a/packages/create/src/frameworks/solid/project/base/src/components/Header.tsx.ejs b/packages/create/src/frameworks/solid/project/base/src/components/Header.tsx.ejs index 85ec43d9..e91489d8 100644 --- a/packages/create/src/frameworks/solid/project/base/src/components/Header.tsx.ejs +++ b/packages/create/src/frameworks/solid/project/base/src/components/Header.tsx.ejs @@ -1,3 +1,4 @@ +<% if (!includeExamples) { ignoreFile(); return; } %> import { Link } from '@tanstack/solid-router' <% for(const integration of integrations.filter(i => i.type === 'header-user')) { %> import <%= integration.jsName %> from '<%= relativePath(integration.path) %>' diff --git a/packages/create/src/frameworks/solid/project/base/src/routes/__root.tsx.ejs b/packages/create/src/frameworks/solid/project/base/src/routes/__root.tsx.ejs index e8f730c2..3fbd4358 100644 --- a/packages/create/src/frameworks/solid/project/base/src/routes/__root.tsx.ejs +++ b/packages/create/src/frameworks/solid/project/base/src/routes/__root.tsx.ejs @@ -16,6 +16,51 @@ function RootComponent() { ) } +<% } else if (!includeExamples) { %> +import { HeadContent, Outlet, Scripts, createRootRouteWithContext } from '@tanstack/solid-router' +import { TanStackRouterDevtools } from '@tanstack/solid-router-devtools' + +<% if (addOnEnabled['solid-ui']) { %> +import "@fontsource/inter/400.css" +<% } %> + +import { HydrationScript } from 'solid-js/web' +import { Suspense } from 'solid-js' + +<% for(const addOn of addOns) { + for(const init of addOn.main?.initialize || []) { %> + <%- init %> +<% } } %> + +import styleCss from "../styles.css?url"; + +export const Route = createRootRouteWithContext()({ + head: () => ({ + links: [{ rel: "stylesheet", href: styleCss }], + }), + shellComponent: RootComponent, +}) + +function RootComponent() { + return ( + + + + + + + + + + <% for(const integration of integrations.filter(i => i.type === 'layout')) { %> + <<%= integration.jsName %> /> + <% } %> + + + + + ); +} <% } else { %> import { HeadContent, Outlet, Scripts, createRootRouteWithContext } from '@tanstack/solid-router' import { TanStackRouterDevtools } from '@tanstack/solid-router-devtools' diff --git a/packages/create/src/frameworks/solid/project/base/src/routes/about.tsx.ejs b/packages/create/src/frameworks/solid/project/base/src/routes/about.tsx.ejs index 48d300de..60072ee9 100644 --- a/packages/create/src/frameworks/solid/project/base/src/routes/about.tsx.ejs +++ b/packages/create/src/frameworks/solid/project/base/src/routes/about.tsx.ejs @@ -1,3 +1,4 @@ +<% if (!includeExamples) { ignoreFile(); return; } %> import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/about')({ diff --git a/packages/create/src/frameworks/solid/project/base/src/routes/index.tsx.ejs b/packages/create/src/frameworks/solid/project/base/src/routes/index.tsx.ejs index 5d1367b3..dc0e5446 100644 --- a/packages/create/src/frameworks/solid/project/base/src/routes/index.tsx.ejs +++ b/packages/create/src/frameworks/solid/project/base/src/routes/index.tsx.ejs @@ -1,3 +1,19 @@ +<% if (!includeExamples) { %> +import { createFileRoute } from '@tanstack/solid-router' + +export const Route = createFileRoute('/')({ component: Home }) + +function Home() { + return ( +
+

Welcome to TanStack Start

+

+ Edit src/routes/index.tsx to get started. +

+
+ ) +} +<% } else { %> import { createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/')({ component: App }) @@ -66,3 +82,4 @@ function App() { ) } +<% } %> diff --git a/packages/create/src/frameworks/solid/project/base/src/styles.css.ejs b/packages/create/src/frameworks/solid/project/base/src/styles.css.ejs index a1952e4a..34fd84bb 100644 --- a/packages/create/src/frameworks/solid/project/base/src/styles.css.ejs +++ b/packages/create/src/frameworks/solid/project/base/src/styles.css.ejs @@ -1,3 +1,20 @@ +<% if (!includeExamples) { %> +@import 'tailwindcss'; + +* { + box-sizing: border-box; +} + +html, +body, +#app { + min-height: 100%; +} + +body { + margin: 0; +} +<% } else { %> @import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,700&family=Manrope:wght@400;500;600;700;800&display=swap'); @import 'tailwindcss'; @@ -193,3 +210,4 @@ code { transform: translateY(0); } } +<% } %> diff --git a/packages/create/src/template-file.ts b/packages/create/src/template-file.ts index dfd62236..520a4d23 100644 --- a/packages/create/src/template-file.ts +++ b/packages/create/src/template-file.ts @@ -137,6 +137,7 @@ export function createTemplateFile(environment: Environment, options: Options) { fileRouter: options.mode === 'file-router', codeRouter: options.mode === 'code-router', routerOnly: options.routerOnly === true, + includeExamples: options.includeExamples !== false, addOnEnabled, addOnOption: options.addOnOptions, addOns: options.chosenAddOns, diff --git a/packages/create/tests/template-context.test.ts b/packages/create/tests/template-context.test.ts index 6d08c8d8..4c31cff6 100644 --- a/packages/create/tests/template-context.test.ts +++ b/packages/create/tests/template-context.test.ts @@ -311,4 +311,67 @@ export const db = testAddon(/* connection */)` expect(output.files['/test/src/db/__sqlite__index.ts']).toBeUndefined() expect(output.files['/test/src/db/__postgres__index.ts']).toBeUndefined() }) +}) + +describe('Template Context - includeExamples', () => { + it('should default includeExamples to true when undefined', async () => { + const { environment, output } = createMemoryEnvironment() + const templateFile = createTemplateFile(environment, simpleOptions) + environment.startRun() + await templateFile( + 'test.txt.ejs', + '<%= includeExamples ? "with-examples" : "no-examples" %>', + ) + environment.finishRun() + + expect(output.files['/test/test.txt']).toEqual('with-examples') + }) + + it('should set includeExamples to false when options.includeExamples is false', async () => { + const { environment, output } = createMemoryEnvironment() + const templateFile = createTemplateFile(environment, { + ...simpleOptions, + includeExamples: false, + }) + environment.startRun() + await templateFile( + 'test.txt.ejs', + '<%= includeExamples ? "with-examples" : "no-examples" %>', + ) + environment.finishRun() + + expect(output.files['/test/test.txt']).toEqual('no-examples') + }) + + it('should ignore files when includeExamples is false', async () => { + const { environment, output } = createMemoryEnvironment() + const templateFile = createTemplateFile(environment, { + ...simpleOptions, + includeExamples: false, + }) + environment.startRun() + await templateFile( + 'about.ts.ejs', + '<% if (!includeExamples) { ignoreFile(); return; } %>\nexport const about = true', + ) + environment.finishRun() + + expect(output.files['/test/about.ts']).toBeUndefined() + }) + + it('should include files when includeExamples is true', async () => { + const { environment, output } = createMemoryEnvironment() + const templateFile = createTemplateFile(environment, { + ...simpleOptions, + includeExamples: true, + }) + environment.startRun() + await templateFile( + 'about.ts.ejs', + '<% if (!includeExamples) { ignoreFile(); return; } %>\nexport const about = true', + ) + environment.finishRun() + + expect(output.files['/test/about.ts']).toBeDefined() + }) }) \ No newline at end of file From e054017daeb7cba1699aff06cb27e929e5e7bf98 Mon Sep 17 00:00:00 2001 From: wyMinLwin Date: Sun, 12 Apr 2026 01:32:41 +0700 Subject: [PATCH 4/5] fix(create): move HeadContent to head block in Solid root template --- .../frameworks/solid/project/base/src/routes/__root.tsx.ejs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/create/src/frameworks/solid/project/base/src/routes/__root.tsx.ejs b/packages/create/src/frameworks/solid/project/base/src/routes/__root.tsx.ejs index 3fbd4358..949f34df 100644 --- a/packages/create/src/frameworks/solid/project/base/src/routes/__root.tsx.ejs +++ b/packages/create/src/frameworks/solid/project/base/src/routes/__root.tsx.ejs @@ -46,9 +46,9 @@ function RootComponent() { + - @@ -92,9 +92,9 @@ function RootComponent() { + -
From e719c2adb41fd6e31642612477fb108507311c2e Mon Sep 17 00:00:00 2001 From: Tanner Linsley Date: Mon, 20 Apr 2026 01:14:00 -0600 Subject: [PATCH 5/5] fix(create): broaden demo file matching to catch all demo-prefixed files The directory-specific filter missed demo support files in /store/, /public/, at src root (demo.index.css), and example assets (example-guitar-*.jpg). Replace directory-specific checks with a filename-prefix match so any file named demo.*, demo-*, example.*, or example-* gets stripped when users opt out of demo pages, regardless of which directory it lives in. Add a regression test that exercises demo files across lib, hooks, data, components, store, public, and routes. --- .changeset/fix-demo-file-leaks.md | 12 ++++ packages/create/src/create-app.ts | 25 ++++---- packages/create/tests/create-app.test.ts | 77 ++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 12 deletions(-) create mode 100644 .changeset/fix-demo-file-leaks.md diff --git a/.changeset/fix-demo-file-leaks.md b/.changeset/fix-demo-file-leaks.md new file mode 100644 index 00000000..b960b9e7 --- /dev/null +++ b/.changeset/fix-demo-file-leaks.md @@ -0,0 +1,12 @@ +--- +'@tanstack/create': patch +--- + +Fix demo/example files leaking into projects when users opt out of demo pages. + +- Strip add-on demo support files in `src/lib/`, `src/hooks/`, `src/data/`, `src/components/`, `src/store/`, and any `demo.*` / `demo-*` / `example.*` / `example-*` files. +- Strip example image assets under `public/`. +- Generate a minimal base starter (no Header, Footer, ThemeToggle, about page, or styled index page) when declining demo/example pages. +- Render Better Auth header-user component as `null` when its demo route is excluded, instead of linking to a non-existent route. + +Closes #422, #409. diff --git a/packages/create/src/create-app.ts b/packages/create/src/create-app.ts index 8d978c9e..6551ce8c 100644 --- a/packages/create/src/create-app.ts +++ b/packages/create/src/create-app.ts @@ -20,19 +20,20 @@ import type { Environment, FileBundleHandler, Options } from './types.js' function isDemoFilePath(path?: string) { if (!path) return false const normalized = path.replace(/\\/g, '/') - return ( + + if ( normalized.includes('/routes/demo/') || - normalized.includes('/routes/demo.') || - normalized.includes('/routes/example/') || - normalized.includes('/routes/example.') || - normalized.includes('/lib/demo-') || - normalized.includes('/lib/demo.') || - normalized.includes('/hooks/demo-') || - normalized.includes('/hooks/demo.') || - normalized.includes('/data/demo-') || - normalized.includes('/data/demo.') || - normalized.includes('/components/demo-') || - normalized.includes('/components/demo.') + normalized.includes('/routes/example/') + ) { + return true + } + + const filename = normalized.split('/').pop() || '' + return ( + filename.startsWith('demo.') || + filename.startsWith('demo-') || + filename.startsWith('example.') || + filename.startsWith('example-') ) } diff --git a/packages/create/tests/create-app.test.ts b/packages/create/tests/create-app.test.ts index 2aef8107..6d77f1c5 100644 --- a/packages/create/tests/create-app.test.ts +++ b/packages/create/tests/create-app.test.ts @@ -117,4 +117,81 @@ describe('createApp', () => { expect(output.files['/src/test2.txt']).toEqual('base64::aGVsbG8=') expect(output.commands.some(({ command }) => command === 'echo')).toBe(true) }) + + it('should strip demo files from add-ons when includeExamples is false', async () => { + const { environment, output } = createMemoryEnvironment() + + const demoFiles = [ + 'src/routes/demo/form.simple.tsx', + 'src/routes/demo.form.tsx', + 'src/routes/example.chat.tsx', + 'src/components/demo-AIAssistant.tsx', + 'src/components/demo.FormComponents.tsx', + 'src/hooks/demo-useAudioRecorder.ts', + 'src/hooks/demo.form.ts', + 'src/lib/demo-store.ts', + 'src/lib/demo.ai-hook.ts', + 'src/data/demo-guitars.ts', + 'src/store/demo.hooks.ts', + 'src/store/demo.store.ts', + 'src/demo.index.css', + 'public/demo-neon.svg', + 'public/example-guitar-flowers.jpg', + ] + const keepFiles = [ + 'src/routes/index.tsx', + 'src/components/Header.tsx', + 'src/lib/utils.ts', + ] + const allFiles = [...demoFiles, ...keepFiles] + + await createApp(environment, { + ...simpleOptions, + includeExamples: false, + chosenAddOns: [ + { + id: 'test-addon', + type: 'add-on', + phase: 'add-on', + packageAdditions: { dependencies: {}, devDependencies: {} }, + routes: [], + integrations: [], + getFiles: () => allFiles, + getFileContents: () => 'content', + getDeletedFiles: () => [], + } as unknown as AddOn, + ], + } as Options) + + for (const file of demoFiles) { + expect(output.files[`/${file}`]).toBeUndefined() + } + for (const file of keepFiles) { + expect(output.files[`/${file}`]).toBeDefined() + } + }) + + it('should keep demo files from add-ons when includeExamples is true', async () => { + const { environment, output } = createMemoryEnvironment() + + await createApp(environment, { + ...simpleOptions, + includeExamples: true, + chosenAddOns: [ + { + id: 'test-addon', + type: 'add-on', + phase: 'add-on', + packageAdditions: { dependencies: {}, devDependencies: {} }, + routes: [], + integrations: [], + getFiles: () => ['src/components/demo-AIAssistant.tsx'], + getFileContents: () => 'content', + getDeletedFiles: () => [], + } as unknown as AddOn, + ], + } as Options) + + expect(output.files['/src/components/demo-AIAssistant.tsx']).toBeDefined() + }) })