Skip to content

Commit 82f31a6

Browse files
fix(create): generate starters that pass Biome lint (#570)
1 parent 3c6ba4e commit 82f31a6

5 files changed

Lines changed: 75 additions & 24 deletions

File tree

‎.changeset/clean-biome-starters.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@tanstack/create': patch
3+
---
4+
5+
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.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { execFile } from 'node:child_process'
2+
import { promisify } from 'node:util'
3+
import { test } from '@playwright/test'
4+
import { createReactAppFixture } from './helpers'
5+
6+
const execFileAsync = promisify(execFile)
7+
8+
for (const framework of ['react', 'solid'] as const) {
9+
for (const blank of [false, true]) {
10+
test(`@blocking lints a ${blank ? 'blank' : 'standard'} ${framework} Biome app`, async () => {
11+
const fixture = await createReactAppFixture({
12+
appName: `${framework}-biome-${blank ? 'blank' : 'standard'}`,
13+
framework,
14+
blank,
15+
toolchain: 'biome',
16+
packageManager: 'pnpm',
17+
skipDevServer: true,
18+
runQualityGatesChecks: true,
19+
})
20+
try {
21+
await execFileAsync('pnpm', ['run', 'lint'], {
22+
cwd: fixture.appDir,
23+
timeout: 60_000,
24+
})
25+
} finally {
26+
await fixture.stop()
27+
await fixture.cleanup()
28+
}
29+
})
30+
}
31+
}

‎packages/cli/tests-e2e/helpers.ts‎

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ type CreateAppFixtureOptions = {
2525
bundler?: 'vite' | 'rsbuild'
2626
packageManager?: 'pnpm' | 'npm' | 'yarn' | 'bun' | 'deno'
2727
blank?: boolean
28+
toolchain?: string
2829
deployment?: string
2930
routerOnly?: boolean
3031
template?: string
@@ -139,7 +140,9 @@ function waitForDevServerURL(getStdout: () => string, timeoutMs = 90_000) {
139140
}
140141

141142
if (Date.now() - started > timeoutMs) {
142-
rejectPromise(new Error('Timed out waiting for dev server URL in output'))
143+
rejectPromise(
144+
new Error('Timed out waiting for dev server URL in output'),
145+
)
143146
return
144147
}
145148

@@ -353,15 +356,20 @@ export async function createAppFixture(
353356
}
354357

355358
const totalMs = now() - start
356-
const summary = timings.map(([label, ms]) => `${label}=${Math.round(ms)}ms`).join(' | ')
357-
console.log(`[e2e:timing] ${options.appName} :: ${summary} | total=${Math.round(totalMs)}ms`)
359+
const summary = timings
360+
.map(([label, ms]) => `${label}=${Math.round(ms)}ms`)
361+
.join(' | ')
362+
console.log(
363+
`[e2e:timing] ${options.appName} :: ${summary} | total=${Math.round(totalMs)}ms`,
364+
)
358365
}
359366

360367
const rootDir = await mkdtemp(join(tmpdir(), 'tanstack-cli-e2e-'))
361368
const {
362369
appName,
363370
blank = false,
364371
deployment,
372+
toolchain,
365373
template,
366374
addOns,
367375
afterCreate,
@@ -399,6 +407,10 @@ export async function createAppFixture(
399407
createArgs.push('--blank', '--yes', '--no-intent')
400408
}
401409

410+
if (toolchain) {
411+
createArgs.push('--toolchain', toolchain)
412+
}
413+
402414
if (deployment) {
403415
createArgs.push('--deployment', deployment)
404416
}
@@ -413,16 +425,12 @@ export async function createAppFixture(
413425

414426
try {
415427
const createStartedAt = now()
416-
await runCommand(
417-
process.execPath,
418-
createArgs,
419-
{
420-
cwd: rootDir,
421-
env: {
422-
CI: '1',
423-
},
428+
await runCommand(process.execPath, createArgs, {
429+
cwd: rootDir,
430+
env: {
431+
CI: '1',
424432
},
425-
)
433+
})
426434
mark('create', createStartedAt)
427435

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

432440
if (postCreateAddOns?.length) {
433441
const postAddOnsStartedAt = now()
434-
await runCommand(process.execPath, [cliDistPath, 'add', ...postCreateAddOns, '--forced'], {
435-
cwd: appDir,
436-
env: {
437-
CI: '1',
442+
await runCommand(
443+
process.execPath,
444+
[cliDistPath, 'add', ...postCreateAddOns, '--forced'],
445+
{
446+
cwd: appDir,
447+
env: {
448+
CI: '1',
449+
},
438450
},
439-
})
451+
)
440452

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

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

568-
if (errorText.includes('ERR_ABORTED') || isBlockedByClientError(errorText)) {
580+
if (
581+
errorText.includes('ERR_ABORTED') ||
582+
isBlockedByClientError(errorText)
583+
) {
569584
return
570585
}
571586

‎packages/create/src/frameworks/react/project/base/src/routes/__root.tsx.ejs‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ function RootComponent() {
3333
<% } else if (!includeExamples || blank) { %>
3434
<% let hasContext = addOnEnabled["apollo-client"] || addOnEnabled["tanstack-query"]; %>
3535
import {
36-
HeadContent, Scripts, <% if (hasContext) { %>createRootRouteWithContext<% } else { %>createRootRoute<% } %> } from '@tanstack/react-router'
36+
HeadContent, Scripts, <% if (addOnEnabled.themer) { %>ScriptOnce, <% } %><% if (hasContext) { %>createRootRouteWithContext<% } else { %>createRootRoute<% } %> } from '@tanstack/react-router'
3737
<% if (showDevtools) { %>import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools';
3838
import { TanStackDevtools } from '@tanstack/react-devtools'
3939
<% } %>
@@ -103,7 +103,7 @@ function RootDocument({ children }: { children: React.ReactNode }) {
103103
return (
104104
<% if (addOnEnabled.paraglide) { %><html lang={getLocale()}<% if (addOnEnabled.themer) { %> suppressHydrationWarning<% } %>><% } else { %><html lang="en"<% if (addOnEnabled.themer) { %> suppressHydrationWarning<% } %>><% } %>
105105
<head>
106-
<% if (addOnEnabled.themer) { %><script dangerouslySetInnerHTML={{ __html: THEME_INIT_SCRIPT }} /><% } %>
106+
<% if (addOnEnabled.themer) { %><ScriptOnce>{THEME_INIT_SCRIPT}</ScriptOnce><% } %>
107107
<HeadContent />
108108
</head>
109109
<body>
@@ -131,7 +131,7 @@ function RootDocument({ children }: { children: React.ReactNode }) {
131131
<% } else { %>
132132
<% let hasContext = addOnEnabled["apollo-client"] || addOnEnabled["tanstack-query"]; %>
133133
import {
134-
HeadContent, Scripts, <% if (hasContext) { %>createRootRouteWithContext<% } else { %>createRootRoute<% } %> } from '@tanstack/react-router'
134+
HeadContent, Scripts, ScriptOnce, <% if (hasContext) { %>createRootRouteWithContext<% } else { %>createRootRoute<% } %> } from '@tanstack/react-router'
135135
import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools';
136136
import { TanStackDevtools } from '@tanstack/react-devtools'
137137
import Footer from '../components/Footer'
@@ -201,7 +201,7 @@ function RootDocument({ children }: { children: React.ReactNode }) {
201201
return (
202202
<% if (addOnEnabled.paraglide) { %><html lang={getLocale()} suppressHydrationWarning><% } else { %><html lang="en" suppressHydrationWarning><% } %>
203203
<head>
204-
<script dangerouslySetInnerHTML={{ __html: THEME_INIT_SCRIPT }} />
204+
<ScriptOnce>{THEME_INIT_SCRIPT}</ScriptOnce>
205205
<HeadContent />
206206
</head>
207207
<body className="font-sans antialiased [overflow-wrap:anywhere] selection:bg-[rgba(79,184,178,0.24)]">

‎packages/create/src/frameworks/solid/project/base/src/routes/__root.tsx.ejs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export const Route = createRootRouteWithContext()({
4848
4949
function RootComponent() {
5050
return (
51-
<html>
51+
<html lang="en">
5252
<head>
5353
<HydrationScript />
5454
<HeadContent />
@@ -94,7 +94,7 @@ export const Route = createRootRouteWithContext()({
9494
9595
function RootComponent() {
9696
return (
97-
<html>
97+
<html lang="en">
9898
<head>
9999
<HydrationScript />
100100
<HeadContent />

0 commit comments

Comments
 (0)