Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/clean-biome-starters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/create': patch
---

Generate React and Solid starters that pass their Biome lint checks. Use Router's nonce-aware theme script helper in React and set the default document language in Solid.
31 changes: 31 additions & 0 deletions packages/cli/tests-e2e/biome-smoke.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { execFile } from 'node:child_process'
import { promisify } from 'node:util'
import { test } from '@playwright/test'
import { createReactAppFixture } from './helpers'

const execFileAsync = promisify(execFile)

for (const framework of ['react', 'solid'] as const) {
for (const blank of [false, true]) {
test(`@blocking lints a ${blank ? 'blank' : 'standard'} ${framework} Biome app`, async () => {
const fixture = await createReactAppFixture({
appName: `${framework}-biome-${blank ? 'blank' : 'standard'}`,
framework,
blank,
toolchain: 'biome',
packageManager: 'pnpm',
skipDevServer: true,
runQualityGatesChecks: true,
})
try {
await execFileAsync('pnpm', ['run', 'lint'], {
cwd: fixture.appDir,
timeout: 60_000,
})
} finally {
await fixture.stop()
await fixture.cleanup()
}
})
}
}
51 changes: 33 additions & 18 deletions packages/cli/tests-e2e/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type CreateAppFixtureOptions = {
bundler?: 'vite' | 'rsbuild'
packageManager?: 'pnpm' | 'npm' | 'yarn' | 'bun' | 'deno'
blank?: boolean
toolchain?: string
deployment?: string
routerOnly?: boolean
template?: string
Expand Down Expand Up @@ -139,7 +140,9 @@ function waitForDevServerURL(getStdout: () => string, timeoutMs = 90_000) {
}

if (Date.now() - started > timeoutMs) {
rejectPromise(new Error('Timed out waiting for dev server URL in output'))
rejectPromise(
new Error('Timed out waiting for dev server URL in output'),
)
return
}

Expand Down Expand Up @@ -353,15 +356,20 @@ export async function createAppFixture(
}

const totalMs = now() - start
const summary = timings.map(([label, ms]) => `${label}=${Math.round(ms)}ms`).join(' | ')
console.log(`[e2e:timing] ${options.appName} :: ${summary} | total=${Math.round(totalMs)}ms`)
const summary = timings
.map(([label, ms]) => `${label}=${Math.round(ms)}ms`)
.join(' | ')
console.log(
`[e2e:timing] ${options.appName} :: ${summary} | total=${Math.round(totalMs)}ms`,
)
}

const rootDir = await mkdtemp(join(tmpdir(), 'tanstack-cli-e2e-'))
const {
appName,
blank = false,
deployment,
toolchain,
template,
addOns,
afterCreate,
Expand Down Expand Up @@ -399,6 +407,10 @@ export async function createAppFixture(
createArgs.push('--blank', '--yes', '--no-intent')
}

if (toolchain) {
createArgs.push('--toolchain', toolchain)
}

if (deployment) {
createArgs.push('--deployment', deployment)
}
Expand All @@ -413,16 +425,12 @@ export async function createAppFixture(

try {
const createStartedAt = now()
await runCommand(
process.execPath,
createArgs,
{
cwd: rootDir,
env: {
CI: '1',
},
await runCommand(process.execPath, createArgs, {
cwd: rootDir,
env: {
CI: '1',
},
)
})
mark('create', createStartedAt)

await afterCreate?.(appDir)
Expand All @@ -431,12 +439,16 @@ export async function createAppFixture(

if (postCreateAddOns?.length) {
const postAddOnsStartedAt = now()
await runCommand(process.execPath, [cliDistPath, 'add', ...postCreateAddOns, '--forced'], {
cwd: appDir,
env: {
CI: '1',
await runCommand(
process.execPath,
[cliDistPath, 'add', ...postCreateAddOns, '--forced'],
{
cwd: appDir,
env: {
CI: '1',
},
},
})
)

mark('post-add-ons', postAddOnsStartedAt)

Expand Down Expand Up @@ -565,7 +577,10 @@ export function attachRuntimeGuards(page: Page, appUrl: string): RuntimeGuards {
}
const errorText = request.failure()?.errorText || 'unknown error'

if (errorText.includes('ERR_ABORTED') || isBlockedByClientError(errorText)) {
if (
errorText.includes('ERR_ABORTED') ||
isBlockedByClientError(errorText)
) {
return
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ function RootComponent() {
<% } else if (!includeExamples || blank) { %>
<% let hasContext = addOnEnabled["apollo-client"] || addOnEnabled["tanstack-query"]; %>
import {
HeadContent, Scripts, <% if (hasContext) { %>createRootRouteWithContext<% } else { %>createRootRoute<% } %> } from '@tanstack/react-router'
HeadContent, Scripts, <% if (addOnEnabled.themer) { %>ScriptOnce, <% } %><% if (hasContext) { %>createRootRouteWithContext<% } else { %>createRootRoute<% } %> } from '@tanstack/react-router'
<% if (showDevtools) { %>import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools';
import { TanStackDevtools } from '@tanstack/react-devtools'
<% } %>
Expand Down Expand Up @@ -103,7 +103,7 @@ function RootDocument({ children }: { children: React.ReactNode }) {
return (
<% if (addOnEnabled.paraglide) { %><html lang={getLocale()}<% if (addOnEnabled.themer) { %> suppressHydrationWarning<% } %>><% } else { %><html lang="en"<% if (addOnEnabled.themer) { %> suppressHydrationWarning<% } %>><% } %>
<head>
<% if (addOnEnabled.themer) { %><script dangerouslySetInnerHTML={{ __html: THEME_INIT_SCRIPT }} /><% } %>
<% if (addOnEnabled.themer) { %><ScriptOnce>{THEME_INIT_SCRIPT}</ScriptOnce><% } %>
<HeadContent />
</head>
<body>
Expand Down Expand Up @@ -131,7 +131,7 @@ function RootDocument({ children }: { children: React.ReactNode }) {
<% } else { %>
<% let hasContext = addOnEnabled["apollo-client"] || addOnEnabled["tanstack-query"]; %>
import {
HeadContent, Scripts, <% if (hasContext) { %>createRootRouteWithContext<% } else { %>createRootRoute<% } %> } from '@tanstack/react-router'
HeadContent, Scripts, ScriptOnce, <% if (hasContext) { %>createRootRouteWithContext<% } else { %>createRootRoute<% } %> } from '@tanstack/react-router'
import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools';
import { TanStackDevtools } from '@tanstack/react-devtools'
import Footer from '../components/Footer'
Expand Down Expand Up @@ -201,7 +201,7 @@ function RootDocument({ children }: { children: React.ReactNode }) {
return (
<% if (addOnEnabled.paraglide) { %><html lang={getLocale()} suppressHydrationWarning><% } else { %><html lang="en" suppressHydrationWarning><% } %>
<head>
<script dangerouslySetInnerHTML={{ __html: THEME_INIT_SCRIPT }} />
<ScriptOnce>{THEME_INIT_SCRIPT}</ScriptOnce>
<HeadContent />
</head>
<body className="font-sans antialiased [overflow-wrap:anywhere] selection:bg-[rgba(79,184,178,0.24)]">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export const Route = createRootRouteWithContext()({

function RootComponent() {
return (
<html>
<html lang="en">
<head>
<HydrationScript />
<HeadContent />
Expand Down Expand Up @@ -94,7 +94,7 @@ export const Route = createRootRouteWithContext()({

function RootComponent() {
return (
<html>
<html lang="en">
<head>
<HydrationScript />
<HeadContent />
Expand Down
Loading