create-newt-app 0.35.0 → 0.35.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ import { existsSync, promises } from "fs";
11
11
  import path from "path";
12
12
  import https from "node:https";
13
13
  //#region package.json
14
- var version = "0.35.0";
14
+ var version = "0.35.2";
15
15
  var engines = { "node": ">=24.0.0" };
16
16
  //#endregion
17
17
  //#region src/templates/root/index.ts
@@ -177,6 +177,21 @@ pnpm dev
177
177
  \`\`\`
178
178
 
179
179
  Open [http://localhost:3000](http://localhost:3000).
180
+ <% if (deployment === 'spa') { -%>
181
+
182
+ ## Production build
183
+
184
+ NestJS serves the static export and the API from one origin, so point \`BETTER_AUTH_URL\` at it:
185
+
186
+ \`\`\`sh
187
+ pnpm db:migrate
188
+ pnpm build
189
+ cd apps/api
190
+ BETTER_AUTH_URL=http://localhost:3001 pnpm start:prod
191
+ \`\`\`
192
+
193
+ Open [http://localhost:3001](http://localhost:3001). In a real deployment, set \`BETTER_AUTH_URL\` to the public URL.
194
+ <% } -%>
180
195
 
181
196
  ## Apps
182
197
 
@@ -224,7 +239,18 @@ BETTER_AUTH_SECRET=generate-with-openssl-rand-base64-32
224
239
  # This is NOT the Next.js you know
225
240
 
226
241
  This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in \`node_modules/next/dist/docs/\` before writing any code. Heed deprecation notices.
227
- <!-- END:nextjs-agent-rules --><% if (shadcn) { %>
242
+ <!-- END:nextjs-agent-rules -->
243
+
244
+ <!-- BEGIN:turborepo-agent-rules -->
245
+
246
+ # This is NOT the Turborepo you know
247
+
248
+ Turborepo configuration, task behavior, and CLI commands can vary between installed versions and may differ from your training data. Resolve the \`turbo\` package from this file's directory or relevant workspace; in monorepos, it may not be visible from the repository root. For example, run \`node -p "require.resolve('turbo/package.json')"\` from a workspace that depends on \`turbo\`.
249
+
250
+ Read \`docs/README.md\` inside that installed package first, then read the relevant pages from its \`docs/\` directory before changing Turborepo configuration or commands. Heed deprecation notices. These bundled docs match the installed package version and are available without network access.
251
+
252
+ This block is written and re-added by \`turbo\` before repository-scoped commands when an AI agent is detected. In the Turborepo source repository, its template is defined in \`crates/turborepo-cli/src/cli/agent_guidance.rs\`. Removing the managed block while updates are enabled means a later qualifying invocation will add it again. Set \`"agentGuidance": false\` in the root \`turbo.json\` or \`turbo.jsonc\` to opt out; this does not remove an existing block. Keep the block committed with your work to avoid an uncommitted change on the next agent invocation.
253
+ <!-- END:turborepo-agent-rules --><% if (shadcn) { %>
228
254
 
229
255
  After making changes, run \`pnpm lint:check\` and fix all errors.<% } %>`
230
256
  },
@@ -910,6 +936,7 @@ const auth = { templates: [
910
936
  "name": "@<%= projectName %>/auth",
911
937
  "version": "0.0.0",
912
938
  "private": true,
939
+ "type": "module",
913
940
  "exports": {
914
941
  ".": "./src/index.ts"
915
942
  },
@@ -1011,7 +1038,6 @@ export interface SessionTable {
1011
1038
 
1012
1039
  export interface AccountTable {
1013
1040
  id: string;
1014
- issuer: string;
1015
1041
  accountId: string;
1016
1042
  providerId: string;
1017
1043
  userId: string;
@@ -1228,7 +1254,9 @@ export const db = new Kysely<DB>({
1228
1254
  dialect: new SqliteDialect({ database: driver }),
1229
1255
  });
1230
1256
 
1231
- export type Database = Kysely<DB>;`
1257
+ export type Database = Kysely<DB>;
1258
+
1259
+ export type * from "./schema.js";`
1232
1260
  };
1233
1261
  //#endregion
1234
1262
  //#region src/templates/db/templates/src-index-postgres.ts
@@ -1244,7 +1272,9 @@ export const db = new Kysely<DB>({
1244
1272
  dialect: new PostgresDialect({ pool: driver }),
1245
1273
  });
1246
1274
 
1247
- export type Database = Kysely<DB>;`
1275
+ export type Database = Kysely<DB>;
1276
+
1277
+ export type * from "./schema.js";`
1248
1278
  };
1249
1279
  //#endregion
1250
1280
  //#region src/templates/db/index.ts
@@ -1569,7 +1599,8 @@ const versions = {
1569
1599
  typescript: "6.0.2",
1570
1600
  "typescript-eslint": "^8.50.0",
1571
1601
  "unplugin-swc": "^1.5.9",
1572
- vitest: "^4.1.10"
1602
+ vitest: "^4.1.10",
1603
+ zod: "^4.6.5"
1573
1604
  };
1574
1605
  //#endregion
1575
1606
  //#region src/templates/shadcn-ui/index.ts
@@ -12273,12 +12304,14 @@ import { resolve } from 'path';
12273
12304
  dotenv.config({ path: resolve(process.cwd(), '../../.env') });
12274
12305
  dotenv.config({ path: resolve(process.cwd(), '.env') });
12275
12306
 
12307
+ import { StandardSchemaValidationPipe } from '@nestjs/common';
12276
12308
  import { NestFactory } from '@nestjs/core';
12277
12309
  import { AppModule } from './app.module';
12278
12310
 
12279
12311
  async function bootstrap() {
12280
12312
  const app = await NestFactory.create(AppModule, { bodyParser: false });
12281
12313
  app.setGlobalPrefix('api');
12314
+ app.useGlobalPipes(new StandardSchemaValidationPipe());
12282
12315
  app.enableCors({
12283
12316
  origin: process.env.CORS_ORIGIN ?? 'http://localhost:3000',
12284
12317
  credentials: true,
@@ -12308,14 +12341,7 @@ var todos_service_default = {
12308
12341
  filename: "apps/api/src/todos/todos.service.ts",
12309
12342
  template: `import { Injectable, NotFoundException } from '@nestjs/common';
12310
12343
  import { randomUUID } from 'node:crypto';
12311
- import { db } from '@<%= projectName %>/db';
12312
-
12313
- export interface Todo {
12314
- id: string;
12315
- title: string;
12316
- done: boolean;
12317
- createdAt: string;
12318
- }
12344
+ import { db, type Todo } from '@<%= projectName %>/db';
12319
12345
 
12320
12346
  const columns = ['id', 'title', 'done', 'createdAt'] as const;
12321
12347
 
@@ -12414,6 +12440,15 @@ export interface TodoTable {
12414
12440
  createdAt: Generated<string>;
12415
12441
  }
12416
12442
 
12443
+ // Wire shape of a todo: what the api serves and the web app consumes.
12444
+ // The service converts table rows (raw sqlite values) into this.
12445
+ export interface Todo {
12446
+ id: string;
12447
+ title: string;
12448
+ done: boolean;
12449
+ createdAt: string;
12450
+ }
12451
+
12417
12452
  export interface UserTable {
12418
12453
  id: string;
12419
12454
  name: string;
@@ -12437,7 +12472,6 @@ export interface SessionTable {
12437
12472
 
12438
12473
  export interface AccountTable {
12439
12474
  id: string;
12440
- issuer: string;
12441
12475
  accountId: string;
12442
12476
  providerId: string;
12443
12477
  userId: string;
@@ -12507,6 +12541,7 @@ var todos_controller_default = {
12507
12541
  } from '@nestjs/common';
12508
12542
  import { Session } from '@thallesp/nestjs-better-auth';
12509
12543
  import type { UserSession } from '@thallesp/nestjs-better-auth';
12544
+ import { z } from 'zod';
12510
12545
  import { TodosService } from './todos.service';
12511
12546
 
12512
12547
  @Controller('todos')
@@ -12519,7 +12554,10 @@ export class TodosController {
12519
12554
  }
12520
12555
 
12521
12556
  @Post()
12522
- create(@Session() session: UserSession, @Body('title') title: string) {
12557
+ create(
12558
+ @Session() session: UserSession,
12559
+ @Body('title', { schema: z.string().trim().min(1) }) title: string,
12560
+ ) {
12523
12561
  return this.todosService.create(session.user.id, title);
12524
12562
  }
12525
12563
 
@@ -12601,7 +12639,7 @@ var api_index_default = {
12601
12639
  template: `export { AppModule } from './app.module';
12602
12640
  export { AppService } from './app.service';
12603
12641
  export { TodosService } from './todos/todos.service';
12604
- export type { Todo } from './todos/todos.service';`
12642
+ export type { Todo } from '@<%= projectName %>/db';`
12605
12643
  };
12606
12644
  //#endregion
12607
12645
  //#region src/templates/todo-example/templates/web-todos-route.ts
@@ -12635,6 +12673,7 @@ export async function POST(req: Request) {
12635
12673
  var web_todos_id_route_default = {
12636
12674
  filename: "apps/web/app/api/todos/[id]/route.ts",
12637
12675
  template: `import { NextResponse } from 'next/server';
12676
+ import { HttpException } from '@nestjs/common';
12638
12677
  import { headers } from 'next/headers';
12639
12678
  import { inject } from '@/lib/nest';
12640
12679
  import { auth } from '@<%= projectName %>/auth';
@@ -12645,8 +12684,15 @@ export async function DELETE(_req: Request, { params }: { params: Promise<{ id:
12645
12684
  if (!session) return new NextResponse('Unauthorized', { status: 401 });
12646
12685
  const { id } = await params;
12647
12686
  const todos = await inject(TodosService);
12648
- await todos.remove(session.user.id, id);
12649
- return new NextResponse(null, { status: 204 });
12687
+ try {
12688
+ await todos.remove(session.user.id, id);
12689
+ return new NextResponse(null, { status: 204 });
12690
+ } catch (err) {
12691
+ if (err instanceof HttpException) {
12692
+ return NextResponse.json(err.getResponse(), { status: err.getStatus() });
12693
+ }
12694
+ throw err;
12695
+ }
12650
12696
  }`
12651
12697
  };
12652
12698
  //#endregion
@@ -12654,6 +12700,7 @@ export async function DELETE(_req: Request, { params }: { params: Promise<{ id:
12654
12700
  var web_todos_toggle_route_default = {
12655
12701
  filename: "apps/web/app/api/todos/[id]/toggle/route.ts",
12656
12702
  template: `import { NextResponse } from 'next/server';
12703
+ import { HttpException } from '@nestjs/common';
12657
12704
  import { headers } from 'next/headers';
12658
12705
  import { inject } from '@/lib/nest';
12659
12706
  import { auth } from '@<%= projectName %>/auth';
@@ -12664,7 +12711,14 @@ export async function PATCH(_req: Request, { params }: { params: Promise<{ id: s
12664
12711
  if (!session) return new NextResponse('Unauthorized', { status: 401 });
12665
12712
  const { id } = await params;
12666
12713
  const todos = await inject(TodosService);
12667
- return NextResponse.json(await todos.toggle(session.user.id, id));
12714
+ try {
12715
+ return NextResponse.json(await todos.toggle(session.user.id, id));
12716
+ } catch (err) {
12717
+ if (err instanceof HttpException) {
12718
+ return NextResponse.json(err.getResponse(), { status: err.getStatus() });
12719
+ }
12720
+ throw err;
12721
+ }
12668
12722
  }`
12669
12723
  };
12670
12724
  //#endregion
@@ -12677,25 +12731,28 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
12677
12731
  import { useForm } from '@tanstack/react-form';
12678
12732
  import { authClient } from '@/lib/auth-client';
12679
12733
  import { Button } from '@<%= projectName %>/ui/button';
12734
+ import type { Todo } from '@<%= projectName %>/db';
12680
12735
 
12681
- interface Todo {
12682
- id: string;
12683
- title: string;
12684
- done: boolean;
12736
+ async function request<T>(url: string, init?: RequestInit): Promise<T> {
12737
+ const res = await fetch(url, init);
12738
+ if (!res.ok) throw new Error('Request failed with status ' + res.status);
12739
+ const text = await res.text();
12740
+ // SAFETY: T is the response shape the api wrapper promises for each
12741
+ // endpoint, and every success path returns a JSON body.
12742
+ return (text ? JSON.parse(text) : undefined) as T;
12685
12743
  }
12686
12744
 
12687
12745
  const api = {
12688
- getTodos: (): Promise<Todo[]> => fetch('/api/todos').then((r) => r.json()),
12689
- createTodo: (title: string): Promise<Todo> =>
12690
- fetch('/api/todos', {
12746
+ getTodos: () => request<Todo[]>('/api/todos'),
12747
+ createTodo: (title: string) =>
12748
+ request<Todo>('/api/todos', {
12691
12749
  method: 'POST',
12692
12750
  headers: { 'Content-Type': 'application/json' },
12693
12751
  body: JSON.stringify({ title }),
12694
- }).then((r) => r.json()),
12695
- toggleTodo: (id: string): Promise<Todo> =>
12696
- fetch(\`/api/todos/\${id}/toggle\`, { method: 'PATCH' }).then((r) => r.json()),
12697
- deleteTodo: (id: string): Promise<void> =>
12698
- fetch(\`/api/todos/\${id}\`, { method: 'DELETE' }).then(() => undefined),
12752
+ }),
12753
+ toggleTodo: (id: string) =>
12754
+ request<Todo>(\`/api/todos/\${id}/toggle\`, { method: 'PATCH' }),
12755
+ deleteTodo: (id: string) => request<void>(\`/api/todos/\${id}\`, { method: 'DELETE' }),
12699
12756
  };
12700
12757
 
12701
12758
  export function TodoList({
@@ -12705,7 +12762,7 @@ export function TodoList({
12705
12762
  }) {
12706
12763
  const queryClient = useQueryClient();
12707
12764
 
12708
- const { data: todos = [], isPending } = useQuery({
12765
+ const { data: todos = [], isPending, isError } = useQuery({
12709
12766
  queryKey: ['todos'],
12710
12767
  queryFn: api.getTodos,
12711
12768
  });
@@ -12776,8 +12833,14 @@ export function TodoList({
12776
12833
  </form.Subscribe>
12777
12834
  </form>
12778
12835
 
12836
+ {(createMutation.isError || toggleMutation.isError || deleteMutation.isError) && (
12837
+ <p className="text-sm text-red-400 mb-4">Something went wrong. Try again.</p>
12838
+ )}
12839
+
12779
12840
  {isPending ? (
12780
12841
  <p className="text-sm text-muted-foreground">Loading…</p>
12842
+ ) : isError ? (
12843
+ <p className="text-sm text-red-400">Couldn&apos;t load todos.</p>
12781
12844
  ) : (
12782
12845
  <ul className="divide-y divide-neutral-700">
12783
12846
  {todos.map((todo) => (
@@ -12907,25 +12970,29 @@ import { authClient } from '@/lib/auth-client';
12907
12970
  import { Button } from '@<%= projectName %>/ui/button';
12908
12971
  import { Input } from '@<%= projectName %>/ui/input';
12909
12972
  import { Checkbox } from '@<%= projectName %>/ui/checkbox';
12973
+ import { toast } from 'sonner';
12974
+ import type { Todo } from '@<%= projectName %>/db';
12910
12975
 
12911
- interface Todo {
12912
- id: string;
12913
- title: string;
12914
- done: boolean;
12976
+ async function request<T>(url: string, init?: RequestInit): Promise<T> {
12977
+ const res = await fetch(url, init);
12978
+ if (!res.ok) throw new Error('Request failed with status ' + res.status);
12979
+ const text = await res.text();
12980
+ // SAFETY: T is the response shape the api wrapper promises for each
12981
+ // endpoint, and every success path returns a JSON body.
12982
+ return (text ? JSON.parse(text) : undefined) as T;
12915
12983
  }
12916
12984
 
12917
12985
  const api = {
12918
- getTodos: (): Promise<Todo[]> => fetch('/api/todos').then((r) => r.json()),
12919
- createTodo: (title: string): Promise<Todo> =>
12920
- fetch('/api/todos', {
12986
+ getTodos: () => request<Todo[]>('/api/todos'),
12987
+ createTodo: (title: string) =>
12988
+ request<Todo>('/api/todos', {
12921
12989
  method: 'POST',
12922
12990
  headers: { 'Content-Type': 'application/json' },
12923
12991
  body: JSON.stringify({ title }),
12924
- }).then((r) => r.json()),
12925
- toggleTodo: (id: string): Promise<Todo> =>
12926
- fetch(\`/api/todos/\${id}/toggle\`, { method: 'PATCH' }).then((r) => r.json()),
12927
- deleteTodo: (id: string): Promise<void> =>
12928
- fetch(\`/api/todos/\${id}\`, { method: 'DELETE' }).then(() => undefined),
12992
+ }),
12993
+ toggleTodo: (id: string) =>
12994
+ request<Todo>(\`/api/todos/\${id}/toggle\`, { method: 'PATCH' }),
12995
+ deleteTodo: (id: string) => request<void>(\`/api/todos/\${id}\`, { method: 'DELETE' }),
12929
12996
  };
12930
12997
 
12931
12998
  export function TodoList({
@@ -12935,7 +13002,7 @@ export function TodoList({
12935
13002
  }) {
12936
13003
  const queryClient = useQueryClient();
12937
13004
 
12938
- const { data: todos = [], isPending } = useQuery({
13005
+ const { data: todos = [], isPending, isError } = useQuery({
12939
13006
  queryKey: ['todos'],
12940
13007
  queryFn: api.getTodos,
12941
13008
  });
@@ -12943,16 +13010,19 @@ export function TodoList({
12943
13010
  const createMutation = useMutation({
12944
13011
  mutationFn: api.createTodo,
12945
13012
  onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
13013
+ onError: () => toast.error("Couldn't add todo"),
12946
13014
  });
12947
13015
 
12948
13016
  const toggleMutation = useMutation({
12949
13017
  mutationFn: api.toggleTodo,
12950
13018
  onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
13019
+ onError: () => toast.error("Couldn't update todo"),
12951
13020
  });
12952
13021
 
12953
13022
  const deleteMutation = useMutation({
12954
13023
  mutationFn: api.deleteTodo,
12955
13024
  onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
13025
+ onError: () => toast.error("Couldn't delete todo"),
12956
13026
  });
12957
13027
 
12958
13028
  const form = useForm({
@@ -13009,6 +13079,8 @@ export function TodoList({
13009
13079
 
13010
13080
  {isPending ? (
13011
13081
  <p className="text-sm text-muted-foreground">Loading…</p>
13082
+ ) : isError ? (
13083
+ <p className="text-sm text-destructive">Couldn&apos;t load todos.</p>
13012
13084
  ) : (
13013
13085
  <ul className="divide-y divide-border">
13014
13086
  {todos.map((todo) => (
@@ -13161,25 +13233,28 @@ import * as stylex from '@stylexjs/stylex';
13161
13233
  import { authClient } from '@/lib/auth-client';
13162
13234
  import { Button } from '@<%= projectName %>/ui/button';
13163
13235
  import { colors, radii } from '@<%= projectName %>/ui/tokens.stylex';
13236
+ import type { Todo } from '@<%= projectName %>/db';
13164
13237
 
13165
- interface Todo {
13166
- id: string;
13167
- title: string;
13168
- done: boolean;
13238
+ async function request<T>(url: string, init?: RequestInit): Promise<T> {
13239
+ const res = await fetch(url, init);
13240
+ if (!res.ok) throw new Error('Request failed with status ' + res.status);
13241
+ const text = await res.text();
13242
+ // SAFETY: T is the response shape the api wrapper promises for each
13243
+ // endpoint, and every success path returns a JSON body.
13244
+ return (text ? JSON.parse(text) : undefined) as T;
13169
13245
  }
13170
13246
 
13171
13247
  const api = {
13172
- getTodos: (): Promise<Todo[]> => fetch('/api/todos').then((r) => r.json()),
13173
- createTodo: (title: string): Promise<Todo> =>
13174
- fetch('/api/todos', {
13248
+ getTodos: () => request<Todo[]>('/api/todos'),
13249
+ createTodo: (title: string) =>
13250
+ request<Todo>('/api/todos', {
13175
13251
  method: 'POST',
13176
13252
  headers: { 'Content-Type': 'application/json' },
13177
13253
  body: JSON.stringify({ title }),
13178
- }).then((r) => r.json()),
13179
- toggleTodo: (id: string): Promise<Todo> =>
13180
- fetch(\`/api/todos/\${id}/toggle\`, { method: 'PATCH' }).then((r) => r.json()),
13181
- deleteTodo: (id: string): Promise<void> =>
13182
- fetch(\`/api/todos/\${id}\`, { method: 'DELETE' }).then(() => undefined),
13254
+ }),
13255
+ toggleTodo: (id: string) =>
13256
+ request<Todo>(\`/api/todos/\${id}/toggle\`, { method: 'PATCH' }),
13257
+ deleteTodo: (id: string) => request<void>(\`/api/todos/\${id}\`, { method: 'DELETE' }),
13183
13258
  };
13184
13259
 
13185
13260
  export function TodoList({
@@ -13189,7 +13264,7 @@ export function TodoList({
13189
13264
  }) {
13190
13265
  const queryClient = useQueryClient();
13191
13266
 
13192
- const { data: todos = [], isPending } = useQuery({
13267
+ const { data: todos = [], isPending, isError } = useQuery({
13193
13268
  queryKey: ['todos'],
13194
13269
  queryFn: api.getTodos,
13195
13270
  });
@@ -13260,8 +13335,14 @@ export function TodoList({
13260
13335
  </form.Subscribe>
13261
13336
  </form>
13262
13337
 
13338
+ {(createMutation.isError || toggleMutation.isError || deleteMutation.isError) && (
13339
+ <p {...stylex.props(styles.error)}>Something went wrong. Try again.</p>
13340
+ )}
13341
+
13263
13342
  {isPending ? (
13264
13343
  <p {...stylex.props(styles.muted)}>Loading…</p>
13344
+ ) : isError ? (
13345
+ <p {...stylex.props(styles.error)}>Couldn&apos;t load todos.</p>
13265
13346
  ) : (
13266
13347
  <ul {...stylex.props(styles.list)}>
13267
13348
  {todos.map((todo) => (
@@ -13386,6 +13467,11 @@ const styles = stylex.create({
13386
13467
  fontSize: '0.875rem',
13387
13468
  color: colors.mutedForeground,
13388
13469
  },
13470
+ error: {
13471
+ fontSize: '0.875rem',
13472
+ color: colors.danger,
13473
+ marginBottom: '1rem',
13474
+ },
13389
13475
  });
13390
13476
  `
13391
13477
  };
@@ -13576,11 +13662,18 @@ const todoExampleApi = { templates: [
13576
13662
  db_schema_default,
13577
13663
  db_migration_todos_default
13578
13664
  ] };
13579
- const todoExampleControllers = { templates: [
13580
- todos_controller_default,
13581
- todos_module_controllers_default,
13582
- app_module_controllers_default
13583
- ] };
13665
+ const todoExampleControllers = {
13666
+ templates: [
13667
+ todos_controller_default,
13668
+ todos_module_controllers_default,
13669
+ app_module_controllers_default
13670
+ ],
13671
+ packages: [{
13672
+ package: "zod",
13673
+ module: "apps/api",
13674
+ version: versions.zod
13675
+ }]
13676
+ };
13584
13677
  const todoExampleDi = { templates: [
13585
13678
  web_todos_route_default,
13586
13679
  web_todos_id_route_default,
@@ -13588,9 +13681,23 @@ const todoExampleDi = { templates: [
13588
13681
  app_module_di_default,
13589
13682
  api_index_default
13590
13683
  ] };
13591
- const todoExampleWeb = { templates: [web_todo_list_default, web_page_default] };
13592
- const todoExampleShadcn = { templates: [shadcn_todo_list_default, shadcn_page_default] };
13593
- const todoExampleStylex = { templates: [stylex_todo_list_default, stylex_page_default] };
13684
+ const webDbPackage = {
13685
+ package: "@<%= projectName %>/db",
13686
+ module: "apps/web",
13687
+ version: "workspace:*"
13688
+ };
13689
+ const todoExampleWeb = {
13690
+ templates: [web_todo_list_default, web_page_default],
13691
+ packages: [webDbPackage]
13692
+ };
13693
+ const todoExampleShadcn = {
13694
+ templates: [shadcn_todo_list_default, shadcn_page_default],
13695
+ packages: [webDbPackage]
13696
+ };
13697
+ const todoExampleStylex = {
13698
+ templates: [stylex_todo_list_default, stylex_page_default],
13699
+ packages: [webDbPackage]
13700
+ };
13594
13701
  //#endregion
13595
13702
  //#region src/templates/index.ts
13596
13703
  const staticDir = new URL("./static/", import.meta.url);
@@ -13790,7 +13897,7 @@ function validateNodeVersion(current, requirement) {
13790
13897
  };
13791
13898
  }
13792
13899
  function normalizeProjectName(projectName) {
13793
- return projectName.trim().toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9\-._~]/g, "-").replace(/-+/g, "-").replace(/^[-._]+/, "").replace(/-+$/, "").slice(0, 214);
13900
+ return projectName.trim().normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9\-._~]/g, "-").replace(/-+/g, "-").replace(/^[-._]+/, "").replace(/-+$/, "").slice(0, 214);
13794
13901
  }
13795
13902
  function validateProjectName(projectName) {
13796
13903
  if (!projectName) return {
@@ -14165,12 +14272,16 @@ async function doInit(options) {
14165
14272
  });
14166
14273
  await p.tasks(taskBuilder.tasks);
14167
14274
  p.outro(`Done!`);
14275
+ if (name !== rawName) {
14276
+ console.log(`Note: the project directory is "${name}", not "${rawName}".`);
14277
+ console.log();
14278
+ }
14168
14279
  console.log("Next steps:");
14169
14280
  console.log();
14170
14281
  console.log(chalk.blue(` cd ${name}`));
14171
14282
  if (!options.install) {
14172
14283
  console.log(chalk.blue(` pnpm install`));
14173
- console.log(chalk.blue(` pnpm format`));
14284
+ console.log(chalk.blue(` pnpm format:fix`));
14174
14285
  }
14175
14286
  if (!committed) console.log(chalk.blue(` git commit -m "Initial commit"`));
14176
14287
  console.log(chalk.blue(` pnpm dev`));