create-start-app 0.8.0 → 0.9.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.
@@ -290,7 +290,7 @@ export async function createApp(options, { silent = false, environment, }) {
290
290
  await copyFilesRecursively(environment, addOnDir, targetDir, async (file, targetFileName) => templateFile(addOnDir, file, targetFileName));
291
291
  }
292
292
  if (addOn.command) {
293
- await environment.execute(addOn.command.command, addOn.command.args || [], targetDir);
293
+ await environment.execute(addOn.command.command, addOn.command.args || [], resolve(targetDir));
294
294
  }
295
295
  s?.stop(`${addOn.name} setup complete`);
296
296
  }
@@ -306,8 +306,8 @@ export async function createApp(options, { silent = false, environment, }) {
306
306
  }
307
307
  if (shadcnComponents.size > 0) {
308
308
  s?.start(`Installing shadcn components (${Array.from(shadcnComponents).join(', ')})...`);
309
- await environment.execute('npx', ['shadcn@canary', 'add', ...shadcnComponents], targetDir);
310
- s?.stop(`Installed required shadcn components`);
309
+ await environment.execute('npx', ['shadcn@canary', 'add', '--force', ...shadcnComponents], resolve(targetDir));
310
+ s?.stop(`Installed additional shadcn components`);
311
311
  }
312
312
  }
313
313
  const integrations = [];
@@ -400,7 +400,7 @@ export async function createApp(options, { silent = false, environment, }) {
400
400
  await templateFile(templateDirBase, 'README.md.ejs');
401
401
  // Install dependencies
402
402
  s?.start(`Installing dependencies via ${options.packageManager}...`);
403
- await environment.execute(options.packageManager, ['install'], targetDir);
403
+ await environment.execute(options.packageManager, ['install'], resolve(targetDir));
404
404
  s?.stop(`Installed dependencies`);
405
405
  if (warnings.length > 0) {
406
406
  if (!silent) {
@@ -412,17 +412,17 @@ export async function createApp(options, { silent = false, environment, }) {
412
412
  switch (options.packageManager) {
413
413
  case 'pnpm':
414
414
  // pnpm automatically forwards extra arguments
415
- await environment.execute(options.packageManager, ['run', 'check', '--fix'], targetDir);
415
+ await environment.execute(options.packageManager, ['run', 'check', '--fix'], resolve(targetDir));
416
416
  break;
417
417
  default:
418
- await environment.execute(options.packageManager, ['run', 'check', '--', '--fix'], targetDir);
418
+ await environment.execute(options.packageManager, ['run', 'check', '--', '--fix'], resolve(targetDir));
419
419
  break;
420
420
  }
421
421
  s?.stop(`Applied toolchain ${options.toolchain}...`);
422
422
  }
423
423
  if (options.git) {
424
424
  s?.start(`Initializing git repository...`);
425
- await environment.execute('git', ['init'], targetDir);
425
+ await environment.execute('git', ['init'], resolve(targetDir));
426
426
  s?.stop(`Initialized git repository`);
427
427
  }
428
428
  environment.finishRun();
package/package.json CHANGED
@@ -1,10 +1,9 @@
1
1
  {
2
2
  "name": "create-start-app",
3
- "version": "0.8.0",
3
+ "version": "0.9.2",
4
4
  "description": "Tanstack Application Builder",
5
5
  "bin": "./dist/index.js",
6
6
  "type": "module",
7
- "scripts": {},
8
7
  "repository": {
9
8
  "type": "git",
10
9
  "url": "https://github.com/TanStack/create-tsrouter-app.git"
package/src/create-app.ts CHANGED
@@ -463,7 +463,7 @@ export async function createApp(
463
463
  await environment.execute(
464
464
  addOn.command.command,
465
465
  addOn.command.args || [],
466
- targetDir,
466
+ resolve(targetDir),
467
467
  )
468
468
  }
469
469
 
@@ -487,10 +487,10 @@ export async function createApp(
487
487
  )
488
488
  await environment.execute(
489
489
  'npx',
490
- ['shadcn@canary', 'add', ...shadcnComponents],
491
- targetDir,
490
+ ['shadcn@canary', 'add', '--force', ...shadcnComponents],
491
+ resolve(targetDir),
492
492
  )
493
- s?.stop(`Installed required shadcn components`)
493
+ s?.stop(`Installed additional shadcn components`)
494
494
  }
495
495
  }
496
496
 
@@ -658,7 +658,11 @@ export async function createApp(
658
658
 
659
659
  // Install dependencies
660
660
  s?.start(`Installing dependencies via ${options.packageManager}...`)
661
- await environment.execute(options.packageManager, ['install'], targetDir)
661
+ await environment.execute(
662
+ options.packageManager,
663
+ ['install'],
664
+ resolve(targetDir),
665
+ )
662
666
  s?.stop(`Installed dependencies`)
663
667
 
664
668
  if (warnings.length > 0) {
@@ -675,14 +679,14 @@ export async function createApp(
675
679
  await environment.execute(
676
680
  options.packageManager,
677
681
  ['run', 'check', '--fix'],
678
- targetDir,
682
+ resolve(targetDir),
679
683
  )
680
684
  break
681
685
  default:
682
686
  await environment.execute(
683
687
  options.packageManager,
684
688
  ['run', 'check', '--', '--fix'],
685
- targetDir,
689
+ resolve(targetDir),
686
690
  )
687
691
  break
688
692
  }
@@ -691,7 +695,7 @@ export async function createApp(
691
695
 
692
696
  if (options.git) {
693
697
  s?.start(`Initializing git repository...`)
694
- await environment.execute('git', ['init'], targetDir)
698
+ await environment.execute('git', ['init'], resolve(targetDir))
695
699
  s?.stop(`Initialized git repository`)
696
700
  }
697
701
 
@@ -0,0 +1,300 @@
1
+ import { useStore } from '@tanstack/react-form'
2
+
3
+ import { useFieldContext, useFormContext } from '../hooks/demo.form-context'
4
+ <% if (addOnEnabled.shadcn) { %>
5
+ import { Button } from '@/components/ui/button'
6
+ import { Input } from '@/components/ui/input'
7
+ import { Textarea as ShadcnTextarea } from '@/components/ui/textarea'
8
+ import * as ShadcnSelect from '@/components/ui/select'
9
+ import { Slider as ShadcnSlider } from '@/components/ui/slider'
10
+ import { Switch as ShadcnSwitch } from '@/components/ui/switch'
11
+ import { Label } from '@/components/ui/label'
12
+
13
+ export function SubscribeButton({ label }: { label: string }) {
14
+ const form = useFormContext()
15
+ return (
16
+ <form.Subscribe selector={(state) => state.isSubmitting}>
17
+ {(isSubmitting) => (
18
+ <Button type="submit" disabled={isSubmitting}>
19
+ {label}
20
+ </Button>
21
+ )}
22
+ </form.Subscribe>
23
+ )
24
+ }
25
+
26
+ function ErrorMessages({
27
+ errors,
28
+ }: {
29
+ errors: Array<string | { message: string }>
30
+ }) {
31
+ return (
32
+ <>
33
+ {errors.map((error) => (
34
+ <div
35
+ key={typeof error === 'string' ? error : error.message}
36
+ className="text-red-500 mt-1 font-bold"
37
+ >
38
+ {typeof error === 'string' ? error : error.message}
39
+ </div>
40
+ ))}
41
+ </>
42
+ )
43
+ }
44
+
45
+ export function TextField({
46
+ label,
47
+ placeholder,
48
+ }: {
49
+ label: string
50
+ placeholder?: string
51
+ }) {
52
+ const field = useFieldContext<string>()
53
+ const errors = useStore(field.store, (state) => state.meta.errors)
54
+
55
+ return (
56
+ <div>
57
+ <Label htmlFor={label} className="mb-2 text-xl font-bold">
58
+ {label}
59
+ </Label>
60
+ <Input
61
+ value={field.state.value}
62
+ placeholder={placeholder}
63
+ onBlur={field.handleBlur}
64
+ onChange={(e) => field.handleChange(e.target.value)}
65
+ />
66
+ {field.state.meta.isTouched && <ErrorMessages errors={errors} />}
67
+ </div>
68
+ )
69
+ }
70
+
71
+ export function TextArea({
72
+ label,
73
+ rows = 3,
74
+ }: {
75
+ label: string
76
+ rows?: number
77
+ }) {
78
+ const field = useFieldContext<string>()
79
+ const errors = useStore(field.store, (state) => state.meta.errors)
80
+
81
+ return (
82
+ <div>
83
+ <Label htmlFor={label} className="mb-2 text-xl font-bold">
84
+ {label}
85
+ </Label>
86
+ <ShadcnTextarea
87
+ id={label}
88
+ value={field.state.value}
89
+ onBlur={field.handleBlur}
90
+ rows={rows}
91
+ onChange={(e) => field.handleChange(e.target.value)}
92
+ />
93
+ {field.state.meta.isTouched && <ErrorMessages errors={errors} />}
94
+ </div>
95
+ )
96
+ }
97
+
98
+ export function Select({
99
+ label,
100
+ values,
101
+ placeholder,
102
+ }: {
103
+ label: string
104
+ values: Array<{ label: string; value: string }>
105
+ placeholder?: string
106
+ }) {
107
+ const field = useFieldContext<string>()
108
+ const errors = useStore(field.store, (state) => state.meta.errors)
109
+
110
+ return (
111
+ <div>
112
+ <ShadcnSelect.Select
113
+ name={field.name}
114
+ value={field.state.value}
115
+ onValueChange={(value) => field.handleChange(value)}
116
+ >
117
+ <ShadcnSelect.SelectTrigger className="w-full">
118
+ <ShadcnSelect.SelectValue placeholder={placeholder} />
119
+ </ShadcnSelect.SelectTrigger>
120
+ <ShadcnSelect.SelectContent>
121
+ <ShadcnSelect.SelectGroup>
122
+ <ShadcnSelect.SelectLabel>{label}</ShadcnSelect.SelectLabel>
123
+ {values.map((value) => (
124
+ <ShadcnSelect.SelectItem key={value.value} value={value.value}>
125
+ {value.label}
126
+ </ShadcnSelect.SelectItem>
127
+ ))}
128
+ </ShadcnSelect.SelectGroup>
129
+ </ShadcnSelect.SelectContent>
130
+ </ShadcnSelect.Select>
131
+ {field.state.meta.isTouched && <ErrorMessages errors={errors} />}
132
+ </div>
133
+ )
134
+ }
135
+
136
+ export function Slider({ label }: { label: string }) {
137
+ const field = useFieldContext<number>()
138
+ const errors = useStore(field.store, (state) => state.meta.errors)
139
+
140
+ return (
141
+ <div>
142
+ <Label htmlFor={label} className="mb-2 text-xl font-bold">
143
+ {label}
144
+ </Label>
145
+ <ShadcnSlider
146
+ id={label}
147
+ onBlur={field.handleBlur}
148
+ value={[field.state.value]}
149
+ onValueChange={(value) => field.handleChange(value[0])}
150
+ />
151
+ {field.state.meta.isTouched && <ErrorMessages errors={errors} />}
152
+ </div>
153
+ )
154
+ }
155
+
156
+ export function Switch({ label }: { label: string }) {
157
+ const field = useFieldContext<boolean>()
158
+ const errors = useStore(field.store, (state) => state.meta.errors)
159
+
160
+ return (
161
+ <div>
162
+ <div className="flex items-center gap-2">
163
+ <ShadcnSwitch
164
+ id={label}
165
+ onBlur={field.handleBlur}
166
+ checked={field.state.value}
167
+ onCheckedChange={(checked) => field.handleChange(checked)}
168
+ />
169
+ <Label htmlFor={label}>{label}</Label>
170
+ </div>
171
+ {field.state.meta.isTouched && <ErrorMessages errors={errors} />}
172
+ </div>
173
+ )
174
+ }
175
+
176
+ <% } else { %>
177
+ export function SubscribeButton({ label }: { label: string }) {
178
+ const form = useFormContext()
179
+ return (
180
+ <form.Subscribe selector={(state) => state.isSubmitting}>
181
+ {(isSubmitting) => (
182
+ <button
183
+ type="submit"
184
+ disabled={isSubmitting}
185
+ className="px-6 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition-colors disabled:opacity-50"
186
+ >
187
+ {label}
188
+ </button>
189
+ )}
190
+ </form.Subscribe>
191
+ )
192
+ }
193
+
194
+ function ErrorMessages({
195
+ errors,
196
+ }: {
197
+ errors: Array<string | { message: string }>
198
+ }) {
199
+ return (
200
+ <>
201
+ {errors.map((error) => (
202
+ <div
203
+ key={typeof error === 'string' ? error : error.message}
204
+ className="text-red-500 mt-1 font-bold"
205
+ >
206
+ {typeof error === 'string' ? error : error.message}
207
+ </div>
208
+ ))}
209
+ </>
210
+ )
211
+ }
212
+
213
+ export function TextField({
214
+ label,
215
+ placeholder,
216
+ }: {
217
+ label: string
218
+ placeholder?: string
219
+ }) {
220
+ const field = useFieldContext<string>()
221
+ const errors = useStore(field.store, (state) => state.meta.errors)
222
+
223
+ return (
224
+ <div>
225
+ <label htmlFor={label} className="block font-bold mb-1 text-xl">
226
+ {label}
227
+ <input
228
+ value={field.state.value}
229
+ placeholder={placeholder}
230
+ onBlur={field.handleBlur}
231
+ onChange={(e) => field.handleChange(e.target.value)}
232
+ className="w-full px-4 py-2 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
233
+ />
234
+ </label>
235
+ {field.state.meta.isTouched && <ErrorMessages errors={errors} />}
236
+ </div>
237
+ )
238
+ }
239
+
240
+ export function TextArea({
241
+ label,
242
+ rows = 3,
243
+ }: {
244
+ label: string
245
+ rows?: number
246
+ }) {
247
+ const field = useFieldContext<string>()
248
+ const errors = useStore(field.store, (state) => state.meta.errors)
249
+
250
+ return (
251
+ <div>
252
+ <label htmlFor={label} className="block font-bold mb-1 text-xl">
253
+ {label}
254
+ <textarea
255
+ value={field.state.value}
256
+ onBlur={field.handleBlur}
257
+ rows={rows}
258
+ onChange={(e) => field.handleChange(e.target.value)}
259
+ className="w-full px-4 py-2 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
260
+ />
261
+ </label>
262
+ {field.state.meta.isTouched && <ErrorMessages errors={errors} />}
263
+ </div>
264
+ )
265
+ }
266
+
267
+ export function Select({
268
+ label,
269
+ values,
270
+ }: {
271
+ label: string
272
+ values: Array<{ label: string; value: string }>
273
+ placeholder?: string
274
+ }) {
275
+ const field = useFieldContext<string>()
276
+ const errors = useStore(field.store, (state) => state.meta.errors)
277
+
278
+ return (
279
+ <div>
280
+ <label htmlFor={label} className="block font-bold mb-1 text-xl">
281
+ {label}
282
+ </label>
283
+ <select
284
+ name={field.name}
285
+ value={field.state.value}
286
+ onBlur={field.handleBlur}
287
+ onChange={(e) => field.handleChange(e.target.value)}
288
+ className="w-full px-4 py-2 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
289
+ >
290
+ {values.map((value) => (
291
+ <option key={value.value} value={value.value}>
292
+ {value.label}
293
+ </option>
294
+ ))}
295
+ </select>
296
+ {field.state.meta.isTouched && <ErrorMessages errors={errors} />}
297
+ </div>
298
+ )
299
+ }
300
+ <% } %>
@@ -153,18 +153,21 @@ function AddressForm() {
153
153
  }}
154
154
  >
155
155
  {(field) => (
156
- <field.Select label="Country">
157
- <option value="">Select a country</option>
158
- <option value="US">United States</option>
159
- <option value="CA">Canada</option>
160
- <option value="UK">United Kingdom</option>
161
- <option value="AU">Australia</option>
162
- <option value="DE">Germany</option>
163
- <option value="FR">France</option>
164
- <option value="JP">Japan</option>
165
- </field.Select>
156
+ <field.Select
157
+ label="Country"
158
+ values={[
159
+ { label: 'United States', value: 'US' },
160
+ { label: 'Canada', value: 'CA' },
161
+ { label: 'United Kingdom', value: 'UK' },
162
+ { label: 'Australia', value: 'AU' },
163
+ { label: 'Germany', value: 'DE' },
164
+ { label: 'France', value: 'FR' },
165
+ { label: 'Japan', value: 'JP' },
166
+ ]}
167
+ placeholder="Select a country"
168
+ />
166
169
  )}
167
- </form.AppField>
170
+ </form.AppField>
168
171
 
169
172
  <form.AppField
170
173
  name="phone"
@@ -13,5 +13,14 @@
13
13
  "url": "/demo/form/address",
14
14
  "name": "Address Form"
15
15
  }
16
+ ],
17
+ "shadcnComponents": [
18
+ "button",
19
+ "select",
20
+ "input",
21
+ "textarea",
22
+ "slider",
23
+ "switch",
24
+ "label"
16
25
  ]
17
26
  }
@@ -1,121 +0,0 @@
1
- import { useStore } from '@tanstack/react-form'
2
- import { useFieldContext, useFormContext } from '../hooks/demo.form-context'
3
-
4
- export function SubscribeButton({ label }: { label: string }) {
5
- const form = useFormContext()
6
- return (
7
- <form.Subscribe selector={(state) => state.isSubmitting}>
8
- {(isSubmitting) => (
9
- <button
10
- type="submit"
11
- disabled={isSubmitting}
12
- className="px-6 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition-colors disabled:opacity-50"
13
- >
14
- {label}
15
- </button>
16
- )}
17
- </form.Subscribe>
18
- )
19
- }
20
-
21
- function ErrorMessages({
22
- errors,
23
- }: {
24
- errors: Array<string | { message: string }>
25
- }) {
26
- return (
27
- <>
28
- {errors.map((error) => (
29
- <div
30
- key={typeof error === 'string' ? error : error.message}
31
- className="text-red-500 mt-1 font-bold"
32
- >
33
- {typeof error === 'string' ? error : error.message}
34
- </div>
35
- ))}
36
- </>
37
- )
38
- }
39
-
40
- export function TextField({
41
- label,
42
- placeholder,
43
- }: {
44
- label: string
45
- placeholder?: string
46
- }) {
47
- const field = useFieldContext<string>()
48
- const errors = useStore(field.store, (state) => state.meta.errors)
49
-
50
- return (
51
- <div>
52
- <label htmlFor={label} className="block font-bold mb-1 text-xl">
53
- {label}
54
- <input
55
- value={field.state.value}
56
- placeholder={placeholder}
57
- onBlur={field.handleBlur}
58
- onChange={(e) => field.handleChange(e.target.value)}
59
- className="w-full px-4 py-2 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
60
- />
61
- </label>
62
- {field.state.meta.isTouched && <ErrorMessages errors={errors} />}
63
- </div>
64
- )
65
- }
66
-
67
- export function TextArea({
68
- label,
69
- rows = 3,
70
- }: {
71
- label: string
72
- rows?: number
73
- }) {
74
- const field = useFieldContext<string>()
75
- const errors = useStore(field.store, (state) => state.meta.errors)
76
-
77
- return (
78
- <div>
79
- <label htmlFor={label} className="block font-bold mb-1 text-xl">
80
- {label}
81
- <textarea
82
- value={field.state.value}
83
- onBlur={field.handleBlur}
84
- rows={rows}
85
- onChange={(e) => field.handleChange(e.target.value)}
86
- className="w-full px-4 py-2 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
87
- />
88
- </label>
89
- {field.state.meta.isTouched && <ErrorMessages errors={errors} />}
90
- </div>
91
- )
92
- }
93
-
94
- export function Select({
95
- label,
96
- children,
97
- }: {
98
- label: string
99
- children: React.ReactNode
100
- }) {
101
- const field = useFieldContext<string>()
102
- const errors = useStore(field.store, (state) => state.meta.errors)
103
-
104
- return (
105
- <div>
106
- <label htmlFor={label} className="block font-bold mb-1 text-xl">
107
- {label}
108
- </label>
109
- <select
110
- name={field.name}
111
- value={field.state.value}
112
- onBlur={field.handleBlur}
113
- onChange={(e) => field.handleChange(e.target.value)}
114
- className="w-full px-4 py-2 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500"
115
- >
116
- {children}
117
- </select>
118
- {field.state.meta.isTouched && <ErrorMessages errors={errors} />}
119
- </div>
120
- )
121
- }