Skip to content

fix(create): strip all demo/example files when users opt out#434

Merged
tannerlinsley merged 5 commits intomainfrom
taren/fix-demo-files
Apr 20, 2026
Merged

fix(create): strip all demo/example files when users opt out#434
tannerlinsley merged 5 commits intomainfrom
taren/fix-demo-files

Conversation

@tannerlinsley
Copy link
Copy Markdown
Member

@tannerlinsley tannerlinsley commented Apr 20, 2026

Summary

Closes #422 and #409 — when users select "No" for "Would you like to include demo/example pages?", a lot of demo boilerplate was still getting scaffolded.

This PR combines the work from @WaryaWayne (#431) and @wyMinLwin (#429) into a single unified fix, plus additional coverage for edge cases they missed. Both original commits are preserved with author credit.

What was leaking through

The previous filter only matched files under /routes/demo/ or /routes/demo.*. Demo support files in /lib/, /hooks/, /data/, /components/, /store/, at the src/ root (demo.index.css), and example assets in /public/ (example-guitar-*.jpg, demo-neon.svg) were all being copied into new projects with no routes referencing them. On top of that, the base scaffolding always shipped a Header, Footer, ThemeToggle, styled landing page, and About route — even when the user wanted a minimal starter.

Fixes

From @WaryaWayne's #431:

  • Rename isDemoRoutePathisDemoFilePath and expand it to cover support files in lib, hooks, data, components
  • Extend filtering to add-on integrations (not just routes and files)
  • Convert Better Auth header-user component to an EJS template that renders null when its demo route is excluded, instead of linking to a non-existent route

From @wyMinLwin's #429:

  • Pipe includeExamples into the template context
  • Conditionally render the base Header, Footer, ThemeToggle, About route, styled index route, and styles when demo pages are disabled
  • Add template-context tests for the new flag

Additional cleanup I layered on top:

  • Replace the directory-specific list with a filename-prefix match so any file whose basename starts with demo., demo-, example., or example- gets stripped, regardless of which directory it lives in. This catches the /store/demo.*, /public/example-*, and src/demo.index.css cases the original approach missed.
  • Add a regression test that exercises the filter against demo files across lib, hooks, data, components, store, public, and routes.

Test plan

  • Unit tests: pnpm --filter @tanstack/create test (194 passed)
  • CLI unit tests: pnpm --filter @tanstack/cli test (84 passed)
  • E2E smoke tests: pnpm --filter @tanstack/cli test:e2e (3 passed — create, solid, addons)
  • Manual: tanstack create ... --no-examples --add-ons=ai,form,table,store,db,tanstack-query produces a project with no demo*/example* files in src/ or public/, and no Header/Footer/ThemeToggle in the base scaffolding
  • Manual: tanstack create ... --examples --add-ons=tanstack-query keeps Header/Footer/ThemeToggle and demo routes

Co-authored-by: Abdullahi Mohamed 126521894+WaryaWayne@users.noreply.github.com
Co-authored-by: wyMinLwin waiyanminlwin421@gmail.com

Summary by CodeRabbit

Release Notes

  • New Features

    • Added minimal starter template option that excludes demo and example artifacts, providing a clean foundation for custom projects.
  • Bug Fixes

    • Fixed demo and example files from leaking into projects when users opt out of demo pages.

WaryaWayne and others added 5 commits April 20, 2026 00:55
…hen 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.
…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 a037c4d).
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.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 20, 2026

📝 Walkthrough

Walkthrough

This diff implements a minimal starter template for new TanStack Start projects when users decline demo/example pages. It introduces an includeExamples flag that conditionally includes/excludes demo components, routes, stylesheets, and file assets across React and Solid frameworks, while improving demo file detection logic.

Changes

Cohort / File(s) Summary
Demo Filtering Logic
packages/create/src/create-app.ts, .changeset/fix-demo-file-leaks.md
Enhanced demo file detection by renaming isDemoRoutePath to isDemoFilePath with expanded matching logic for demo/example filenames. Filtering now strips demo files from routes, integration paths, and file lists. Changeset documents stripping of demo artifacts from src/lib/, src/hooks/, src/data/, src/components/, src/store/, and public/ directories.
React Template Variable
packages/create/src/template-file.ts
Added includeExamples template variable (defaults to true) to enable conditional template rendering.
React Components (Conditional Inclusion)
packages/create/src/frameworks/react/project/base/src/components/...
Added includeExamples guards to Header.tsx.ejs, Footer.tsx.ejs, and ThemeToggle.tsx.ejs that prevent file emission when examples are excluded.
React Routes (Conditional Structure)
packages/create/src/frameworks/react/project/base/src/routes/__root.tsx.ejs, about.tsx.ejs, index.tsx.ejs
Modified routes to branch on includeExamples: root route provides full document layout with providers vs. minimal outlet when included; about route is omitted when excluded; index route renders minimal Home component vs. full App component.
React Styling
packages/create/src/frameworks/react/project/base/src/styles.css.ejs
Injected baseline CSS (box-sizing, full-height rules, margin reset) when includeExamples is false; omitted when true.
React Better Auth
packages/create/src/frameworks/react/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx.ejs
Conditionally imports and renders Link to /demo/better-auth only when the demo route exists; returns null otherwise.
Solid Components (Conditional Inclusion)
packages/create/src/frameworks/solid/project/base/src/components/Header.tsx.ejs
Added includeExamples guard preventing file emission when examples are excluded.
Solid Routes (Conditional Structure)
packages/create/src/frameworks/solid/project/base/src/routes/__root.tsx.ejs, about.tsx.ejs, index.tsx.ejs
Modified routes to branch on includeExamples: root route renders full HTML document with HydrationScript, HeadContent, providers, and stylesheet link vs. previous structure; about route omitted when excluded; index route renders minimal Home vs. full App.
Solid Styling
packages/create/src/frameworks/solid/project/base/src/styles.css.ejs
Injected baseline CSS when includeExamples is false; omitted when true.
Solid Better Auth
packages/create/src/frameworks/solid/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx.ejs
Conditionally renders Link fallback to /demo/better-auth and wraps import based on route existence.
Tests
packages/create/tests/create-app.test.ts, template-context.test.ts
Added 77 + 63 lines of tests covering includeExamples behavior: verifies demo file stripping, template variable defaults, file ignoration on conditional templates, and presence/absence of output files.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Hop-hop, the boilerplate hops away!
No demos, no bloat in your project today,
Clean roots and Home pages, so bare and so bright,
Your Start is now starter—lean, minimal, light!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(create): strip all demo/example files when users opt out' directly and clearly summarizes the main change - preventing demo/example file leakage when users decline demo pages.
Linked Issues check ✅ Passed The PR fully addresses issue #422 by implementing comprehensive demo file filtering across all source directories, conditional template rendering based on includeExamples, and minimal scaffolding generation when users opt out of demo pages.
Out of Scope Changes check ✅ Passed All changes are directly related to the linked issue: filtering logic, template conditionals, test coverage, and Better Auth component updates all target demo/example file stripping when users opt out.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch taren/fix-demo-files

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/create/src/frameworks/react/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx.ejs (1)

2-4: Deduplicate the demo-route predicate in template scope.

The same routes.some(...) check appears twice. Consider computing it once to prevent future branch drift.

♻️ Optional cleanup
+<%_ const hasDemoBetterAuthRoute = routes.some((r) => r.url === '/demo/better-auth') _%>
-<%_ if (routes.some(r => r.url === '/demo/better-auth')) { _%>
+<%_ if (hasDemoBetterAuthRoute) { _%>
 import { Link } from "@tanstack/react-router";
 <%_ } _%>
@@
-<%_ if (routes.some(r => r.url === '/demo/better-auth')) { _%>
+<%_ if (hasDemoBetterAuthRoute) { _%>
   return (
     <Link
       to="/demo/better-auth"

Also applies to: 39-50

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/create/src/frameworks/react/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx.ejs`
around lines 2 - 4, Compute the demo-route predicate once and reuse it instead
of repeating routes.some(r => r.url === '/demo/better-auth') in the template;
e.g., define a local variable like hasDemoRoute = routes.some(r => r.url ===
'/demo/better-auth') at the top of header-user.tsx.ejs and replace both
conditional occurrences with hasDemoRoute to avoid duplicate logic and future
drift.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/create/src/create-app.ts`:
- Around line 48-52: filteredRoutes currently filters out demo routes using
isDemoFilePath and checking route.url.startsWith('/demo'), but it misses routes
with URLs under '/example'; update the filter predicate used when building
filteredRoutes (and any related stripExamplesFromOptions logic) to also exclude
route.url that startsWith('/example') (in addition to '/demo') so addOn.routes
entries with route.url like '/example/...' are removed when examples are
disabled; reference the filteredRoutes variable, addOn.routes, isDemoFilePath,
and route.url when making the change.

In `@packages/create/src/frameworks/solid/project/base/src/routes/__root.tsx.ejs`:
- Around line 20-35: The template renders layout integration components (<%=
integration.jsName %>) in the no-examples branch but never imports them; add an
import loop near the top (after existing imports like
createRootRouteWithContext/HydrationScript) that iterates over addOns and their
layout integrations and emits import statements for each integration (using
integration.jsName) so the components referenced in the !includeExamples block
are resolved at compile time.

---

Nitpick comments:
In
`@packages/create/src/frameworks/react/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx.ejs`:
- Around line 2-4: Compute the demo-route predicate once and reuse it instead of
repeating routes.some(r => r.url === '/demo/better-auth') in the template; e.g.,
define a local variable like hasDemoRoute = routes.some(r => r.url ===
'/demo/better-auth') at the top of header-user.tsx.ejs and replace both
conditional occurrences with hasDemoRoute to avoid duplicate logic and future
drift.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4b2b9e94-73b5-401f-bacf-f7c622e88510

📥 Commits

Reviewing files that changed from the base of the PR and between 523c999 and e719c2a.

📒 Files selected for processing (19)
  • .changeset/fix-demo-file-leaks.md
  • packages/create/src/create-app.ts
  • packages/create/src/frameworks/react/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx.ejs
  • packages/create/src/frameworks/react/project/base/src/components/Footer.tsx.ejs
  • packages/create/src/frameworks/react/project/base/src/components/Header.tsx.ejs
  • packages/create/src/frameworks/react/project/base/src/components/ThemeToggle.tsx.ejs
  • packages/create/src/frameworks/react/project/base/src/routes/__root.tsx.ejs
  • packages/create/src/frameworks/react/project/base/src/routes/about.tsx.ejs
  • packages/create/src/frameworks/react/project/base/src/routes/index.tsx.ejs
  • packages/create/src/frameworks/react/project/base/src/styles.css.ejs
  • packages/create/src/frameworks/solid/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx.ejs
  • packages/create/src/frameworks/solid/project/base/src/components/Header.tsx.ejs
  • packages/create/src/frameworks/solid/project/base/src/routes/__root.tsx.ejs
  • packages/create/src/frameworks/solid/project/base/src/routes/about.tsx.ejs
  • packages/create/src/frameworks/solid/project/base/src/routes/index.tsx.ejs
  • packages/create/src/frameworks/solid/project/base/src/styles.css.ejs
  • packages/create/src/template-file.ts
  • packages/create/tests/create-app.test.ts
  • packages/create/tests/template-context.test.ts

Comment on lines 48 to 52
const filteredRoutes = (addOn.routes || []).filter(
(route) =>
!isDemoRoutePath(route.path) &&
!isDemoFilePath(route.path) &&
!(route.url && route.url.startsWith('/demo')),
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Also filter /example route URLs when examples are disabled.

stripExamplesFromOptions currently removes URLs prefixed with /demo only. Routes with route.url like /example/... can still pass through in code-router-style add-ons.

Suggested fix
       const filteredRoutes = (addOn.routes || []).filter(
         (route) =>
           !isDemoFilePath(route.path) &&
-          !(route.url && route.url.startsWith('/demo')),
+          !(
+            route.url &&
+            (route.url.startsWith('/demo') || route.url.startsWith('/example'))
+          ),
       )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const filteredRoutes = (addOn.routes || []).filter(
(route) =>
!isDemoRoutePath(route.path) &&
!isDemoFilePath(route.path) &&
!(route.url && route.url.startsWith('/demo')),
)
const filteredRoutes = (addOn.routes || []).filter(
(route) =>
!isDemoFilePath(route.path) &&
!(
route.url &&
(route.url.startsWith('/demo') || route.url.startsWith('/example'))
),
)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/create/src/create-app.ts` around lines 48 - 52, filteredRoutes
currently filters out demo routes using isDemoFilePath and checking
route.url.startsWith('/demo'), but it misses routes with URLs under '/example';
update the filter predicate used when building filteredRoutes (and any related
stripExamplesFromOptions logic) to also exclude route.url that
startsWith('/example') (in addition to '/demo') so addOn.routes entries with
route.url like '/example/...' are removed when examples are disabled; reference
the filteredRoutes variable, addOn.routes, isDemoFilePath, and route.url when
making the change.

Comment on lines +20 to +35
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";
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Solid no-examples root branch =="
sed -n '19,63p' packages/create/src/frameworks/solid/project/base/src/routes/__root.tsx.ejs

echo
echo "== Integration definitions that may be rendered here =="
rg -n -C2 --glob '*.ts' --glob '*.js' 'type\s*:\s*["'"'"'](layout|provider|devtools)["'"'"']|integrations\s*:' .

echo
echo "== React root import pattern for comparison =="
sed -n '30,45p' packages/create/src/frameworks/react/project/base/src/routes/__root.tsx.ejs

Repository: TanStack/cli

Length of output: 5568


Add missing import loop for layout integrations in the no-examples branch.

The !includeExamples branch at lines 55–57 renders <%= integration.jsName %> for layout integrations but never imports them. Generated projects with Solid layout integrations will fail to compile with unresolved component references. The React root template correctly handles this pattern by importing integrations before use.

Add an import loop after line 17:

 import { TanStackRouterDevtools } from '@tanstack/solid-router-devtools'
 
 <% if (addOnEnabled['solid-ui']) { %>
 import "@fontsource/inter/400.css"
 <% } %>
 
+<% for(const integration of integrations.filter(i => i.type === 'layout')) { %>
+import <%= integration.jsName %> from '<%= relativePath(integration.path, true) %>'
+<% } %>
+
 import { HydrationScript } from 'solid-js/web'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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";
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"
<% } %>
<% for(const integration of integrations.filter(i => i.type === 'layout')) { %>
import <%= integration.jsName %> from '<%= relativePath(integration.path, true) %>'
<% } %>
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";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/create/src/frameworks/solid/project/base/src/routes/__root.tsx.ejs`
around lines 20 - 35, The template renders layout integration components (<%=
integration.jsName %>) in the no-examples branch but never imports them; add an
import loop near the top (after existing imports like
createRootRouteWithContext/HydrationScript) that iterates over addOns and their
layout integrations and emits import statements for each integration (using
integration.jsName) so the components referenced in the !includeExamples block
are resolved at compile time.

@tannerlinsley tannerlinsley merged commit e3de582 into main Apr 20, 2026
4 checks passed
@tannerlinsley tannerlinsley deleted the taren/fix-demo-files branch April 20, 2026 07:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Start] New project from CLI has a lot of boilerplate even when selecting no for option "Would you like to include demo/example pages?"

3 participants