create-tsrouter-app 0.3.0-alpha.7 → 0.3.0-alpha.9

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.
Files changed (40) hide show
  1. package/dist/add-ons.js +9 -0
  2. package/dist/cli.js +32 -17
  3. package/dist/create-app.js +88 -24
  4. package/dist/options.js +62 -48
  5. package/package.json +1 -1
  6. package/src/add-ons.ts +15 -17
  7. package/src/cli.ts +38 -18
  8. package/src/create-app.ts +149 -48
  9. package/src/options.ts +77 -53
  10. package/src/types.ts +2 -1
  11. package/templates/react/add-on/clerk/assets/src/integrations/clerk/header-user.tsx +19 -0
  12. package/templates/react/add-on/clerk/assets/src/integrations/clerk/provider.tsx +18 -0
  13. package/templates/react/add-on/clerk/info.json +0 -16
  14. package/templates/react/add-on/convex/assets/src/integrations/convex/provider.tsx +20 -0
  15. package/templates/react/add-on/convex/info.json +0 -15
  16. package/templates/react/add-on/form/assets/src/routes/{demo.form.tsx → demo.form.tsx.ejs} +15 -3
  17. package/templates/react/add-on/form/info.json +1 -1
  18. package/templates/react/add-on/sentry/info.json +1 -1
  19. package/templates/react/add-on/store/assets/src/routes/{demo.store.page1.tsx → demo.store.page1.tsx.ejs} +14 -6
  20. package/templates/react/add-on/store/assets/src/routes/{demo.store.page2.tsx → demo.store.page2.tsx.ejs} +15 -6
  21. package/templates/react/add-on/store/info.json +1 -1
  22. package/templates/react/add-on/tanstack-query/assets/src/integrations/tanstack-query/layout.tsx +5 -0
  23. package/templates/react/add-on/tanstack-query/assets/src/integrations/tanstack-query/provider.tsx +9 -0
  24. package/templates/react/add-on/tanstack-query/assets/src/routes/demo.tanstack-query.tsx.ejs +38 -0
  25. package/templates/react/add-on/tanstack-query/info.json +1 -19
  26. package/templates/react/{file-router → base}/src/components/Header.tsx.ejs +8 -10
  27. package/templates/react/code-router/src/main.tsx.ejs +17 -1
  28. package/templates/react/file-router/src/routes/__root.tsx.ejs +16 -25
  29. package/templates/solid/add-on/form/assets/src/routes/{demo.form.tsx → demo.form.tsx.ejs} +14 -2
  30. package/templates/solid/add-on/form/info.json +1 -1
  31. package/templates/solid/add-on/store/assets/src/routes/{demo.store.page1.tsx → demo.store.page1.tsx.ejs} +15 -4
  32. package/templates/solid/add-on/store/assets/src/routes/{demo.store.page2.tsx → demo.store.page2.tsx.ejs} +14 -5
  33. package/templates/solid/add-on/store/info.json +1 -1
  34. package/templates/solid/add-on/tanstack-query/assets/src/integrations/tanstack-query/header-user.tsx +5 -0
  35. package/templates/solid/add-on/tanstack-query/assets/src/integrations/tanstack-query/provider.tsx +15 -0
  36. package/templates/solid/add-on/tanstack-query/info.json +0 -18
  37. package/templates/solid/{file-router → base}/src/components/Header.tsx.ejs +7 -5
  38. package/templates/solid/code-router/src/main.tsx.ejs +18 -2
  39. package/templates/solid/file-router/src/routes/__root.tsx.ejs +13 -19
  40. package/templates/react/add-on/tanstack-query/assets/src/routes/demo.tanstack-query.tsx +0 -30
package/dist/add-ons.js CHANGED
@@ -2,6 +2,8 @@ import { readFile } from 'node:fs/promises';
2
2
  import { existsSync, readdirSync, statSync } from 'node:fs';
3
3
  import { resolve } from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
+ import chalk from 'chalk';
6
+ import { DEFAULT_FRAMEWORK } from './constants.js';
5
7
  function isDirectory(path) {
6
8
  return statSync(path).isDirectory();
7
9
  }
@@ -58,3 +60,10 @@ export async function finalizeAddOns(framework, template, chosenAddOnIDs) {
58
60
  }
59
61
  return [...finalAddOnIDs].map((id) => addOns.find((a) => a.id === id));
60
62
  }
63
+ export async function listAddOns(options) {
64
+ const mode = options.template === 'file-router' ? 'file-router' : 'code-router';
65
+ const addOns = await getAllAddOns(options.framework || DEFAULT_FRAMEWORK, mode);
66
+ for (const addOn of addOns) {
67
+ console.log(`${chalk.bold(addOn.id)}: ${addOn.description}`);
68
+ }
69
+ }
package/dist/cli.js CHANGED
@@ -3,6 +3,7 @@ import { intro, log } from '@clack/prompts';
3
3
  import { createApp } from './create-app.js';
4
4
  import { normalizeOptions, promptForOptions } from './options.js';
5
5
  import { SUPPORTED_PACKAGE_MANAGERS } from './package-manager.js';
6
+ import { listAddOns } from './add-ons.js';
6
7
  import { DEFAULT_FRAMEWORK, SUPPORTED_FRAMEWORKS } from './constants.js';
7
8
  export function cli() {
8
9
  const program = new Command();
@@ -32,26 +33,40 @@ export function cli() {
32
33
  return value;
33
34
  })
34
35
  .option('--tailwind', 'add Tailwind CSS', false)
35
- .option('--add-ons', 'pick from a list of available add-ons', false)
36
+ .option('--add-ons [...add-ons]', 'pick from a list of available add-ons (comma separated list)', (value) => {
37
+ let addOns = !!value;
38
+ if (typeof value === 'string') {
39
+ addOns = value.split(',').map((addon) => addon.trim());
40
+ }
41
+ return addOns;
42
+ })
43
+ .option('--list-add-ons', 'list all available add-ons', false)
36
44
  .action(async (projectName, options) => {
37
- try {
38
- const cliOptions = {
39
- projectName,
40
- ...options,
41
- };
42
- let finalOptions = normalizeOptions(cliOptions);
43
- if (finalOptions) {
44
- intro(`Creating a new TanStack app in ${projectName}...`);
45
+ if (options.listAddOns) {
46
+ await listAddOns(options);
47
+ }
48
+ else {
49
+ try {
50
+ const cliOptions = {
51
+ projectName,
52
+ ...options,
53
+ };
54
+ let finalOptions = await normalizeOptions(cliOptions);
55
+ if (finalOptions) {
56
+ intro(`Creating a new TanStack app in ${projectName}...`);
57
+ }
58
+ else {
59
+ intro("Let's configure your TanStack application");
60
+ finalOptions = await promptForOptions(cliOptions);
61
+ }
62
+ await createApp(finalOptions);
45
63
  }
46
- else {
47
- intro("Let's configure your TanStack application");
48
- finalOptions = await promptForOptions(cliOptions);
64
+ catch (error) {
65
+ log.error(error instanceof Error
66
+ ? error.message
67
+ : 'An unknown error occurred');
68
+ process.exit(1);
49
69
  }
50
- await createApp(finalOptions);
51
- }
52
- catch (error) {
53
- log.error(error instanceof Error ? error.message : 'An unknown error occurred');
54
- process.exit(1);
55
70
  }
56
71
  });
57
72
  program.parse();
@@ -25,8 +25,14 @@ function createCopyFiles(targetDir) {
25
25
  }
26
26
  };
27
27
  }
28
+ function jsSafeName(name) {
29
+ return name
30
+ .split(/[^a-zA-Z0-9]/)
31
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
32
+ .join('');
33
+ }
28
34
  function createTemplateFile(projectName, options, targetDir) {
29
- return async function templateFile(templateDir, file, targetFileName) {
35
+ return async function templateFile(templateDir, file, targetFileName, extraTemplateValues) {
30
36
  const templateValues = {
31
37
  packageManager: options.packageManager,
32
38
  projectName: projectName,
@@ -41,9 +47,18 @@ function createTemplateFile(projectName, options, targetDir) {
41
47
  return acc;
42
48
  }, {}),
43
49
  addOns: options.chosenAddOns,
50
+ ...extraTemplateValues,
44
51
  };
45
52
  const template = await readFile(resolve(templateDir, file), 'utf-8');
46
- let content = render(template, templateValues);
53
+ let content = '';
54
+ try {
55
+ content = render(template, templateValues);
56
+ }
57
+ catch (error) {
58
+ console.error(chalk.red(`EJS error in file ${file}`));
59
+ console.error(error);
60
+ process.exit(1);
61
+ }
47
62
  const target = targetFileName ?? file.replace('.ejs', '');
48
63
  if (target.endsWith('.ts') || target.endsWith('.tsx')) {
49
64
  content = await format(content, {
@@ -163,7 +178,6 @@ export async function createApp(options) {
163
178
  const copyFiles = createCopyFiles(targetDir);
164
179
  const templateFile = createTemplateFile(options.projectName, options, targetDir);
165
180
  const isAddOnEnabled = (id) => options.chosenAddOns.find((a) => a.id === id);
166
- log.info(`Creating a new TanStack app in '${basename(targetDir)}'...`);
167
181
  // Make the root directory
168
182
  await mkdir(targetDir, { recursive: true });
169
183
  // Setup the .vscode directory
@@ -195,27 +209,6 @@ export async function createApp(options) {
195
209
  await templateFile(templateDirBase, './vite.config.js.ejs');
196
210
  await templateFile(templateDirBase, './src/styles.css.ejs');
197
211
  copyFiles(templateDirBase, ['./src/logo.svg']);
198
- // Setup the app component. There are four variations, typescript/javascript and tailwind/non-tailwind.
199
- if (options.mode === FILE_ROUTER) {
200
- await templateFile(templateDirRouter, './src/components/Header.tsx.ejs', './src/components/Header.tsx');
201
- await templateFile(templateDirRouter, './src/routes/__root.tsx.ejs', './src/routes/__root.tsx');
202
- await templateFile(templateDirBase, './src/App.tsx.ejs', './src/routes/index.tsx');
203
- }
204
- else {
205
- await templateFile(templateDirBase, './src/App.tsx.ejs', options.typescript ? undefined : './src/App.jsx');
206
- if (options.framework === 'react') {
207
- await templateFile(templateDirBase, './src/App.test.tsx.ejs', options.typescript ? undefined : './src/App.test.jsx');
208
- }
209
- }
210
- // Create the main entry point
211
- if (!isAddOnEnabled('start')) {
212
- if (options.typescript) {
213
- await templateFile(templateDirRouter, './src/main.tsx.ejs');
214
- }
215
- else {
216
- await templateFile(templateDirRouter, './src/main.tsx.ejs', './src/main.jsx');
217
- }
218
- }
219
212
  // Setup the main, reportWebVitals and index.html files
220
213
  if (!isAddOnEnabled('start') && options.framework === 'react') {
221
214
  if (options.typescript) {
@@ -268,6 +261,77 @@ export async function createApp(options) {
268
261
  s.stop(`Installed shadcn components`);
269
262
  }
270
263
  }
264
+ const integrations = [];
265
+ if (existsSync(resolve(targetDir, 'src/integrations'))) {
266
+ for (const integration of readdirSync(resolve(targetDir, 'src/integrations'))) {
267
+ const integrationName = jsSafeName(integration);
268
+ if (existsSync(resolve(targetDir, 'src/integrations', integration, 'layout.tsx'))) {
269
+ integrations.push({
270
+ type: 'layout',
271
+ name: `${integrationName}Layout`,
272
+ path: `integrations/${integration}/layout`,
273
+ });
274
+ }
275
+ if (existsSync(resolve(targetDir, 'src/integrations', integration, 'provider.tsx'))) {
276
+ integrations.push({
277
+ type: 'provider',
278
+ name: `${integrationName}Provider`,
279
+ path: `integrations/${integration}/provider`,
280
+ });
281
+ }
282
+ if (existsSync(resolve(targetDir, 'src/integrations', integration, 'header-user.tsx'))) {
283
+ integrations.push({
284
+ type: 'header-user',
285
+ name: `${integrationName}Header`,
286
+ path: `integrations/${integration}/header-user`,
287
+ });
288
+ }
289
+ }
290
+ }
291
+ const routes = [];
292
+ if (existsSync(resolve(targetDir, 'src/routes'))) {
293
+ for (const file of readdirSync(resolve(targetDir, 'src/routes'))) {
294
+ const name = file.replace(/\.tsx?|\.jsx?/, '');
295
+ const safeRouteName = jsSafeName(name);
296
+ routes.push({
297
+ path: `./routes/${name}`,
298
+ name: safeRouteName,
299
+ });
300
+ }
301
+ }
302
+ // Create the main entry point
303
+ if (!isAddOnEnabled('start')) {
304
+ if (options.typescript) {
305
+ await templateFile(templateDirRouter, './src/main.tsx.ejs', './src/main.tsx', {
306
+ routes,
307
+ integrations,
308
+ });
309
+ }
310
+ else {
311
+ await templateFile(templateDirRouter, './src/main.tsx.ejs', './src/main.jsx', {
312
+ routes,
313
+ integrations,
314
+ });
315
+ }
316
+ }
317
+ // Setup the app component. There are four variations, typescript/javascript and tailwind/non-tailwind.
318
+ if (options.mode === FILE_ROUTER) {
319
+ await templateFile(templateDirRouter, './src/routes/__root.tsx.ejs', './src/routes/__root.tsx', {
320
+ integrations,
321
+ });
322
+ await templateFile(templateDirBase, './src/App.tsx.ejs', './src/routes/index.tsx');
323
+ }
324
+ else {
325
+ await templateFile(templateDirBase, './src/App.tsx.ejs', options.typescript ? undefined : './src/App.jsx');
326
+ if (options.framework === 'react') {
327
+ await templateFile(templateDirBase, './src/App.test.tsx.ejs', options.typescript ? undefined : './src/App.test.jsx');
328
+ }
329
+ }
330
+ if (routes.length > 0) {
331
+ await templateFile(templateDirBase, './src/components/Header.tsx.ejs', './src/components/Header.tsx', {
332
+ integrations,
333
+ });
334
+ }
271
335
  const warnings = [];
272
336
  for (const addOn of options.chosenAddOns) {
273
337
  if (addOn.warning) {
package/dist/options.js CHANGED
@@ -3,24 +3,31 @@ import { DEFAULT_PACKAGE_MANAGER, SUPPORTED_PACKAGE_MANAGERS, getPackageManager,
3
3
  import { CODE_ROUTER, DEFAULT_FRAMEWORK, FILE_ROUTER } from './constants.js';
4
4
  import { finalizeAddOns, getAllAddOns } from './add-ons.js';
5
5
  // If all CLI options are provided, use them directly
6
- export function normalizeOptions(cliOptions) {
6
+ export async function normalizeOptions(cliOptions) {
7
7
  if (cliOptions.projectName) {
8
8
  const typescript = cliOptions.template === 'typescript' ||
9
9
  cliOptions.template === 'file-router' ||
10
10
  cliOptions.framework === 'solid';
11
- const tailwind = cliOptions.tailwind === undefined
11
+ let tailwind = cliOptions.tailwind === undefined
12
12
  ? cliOptions.framework === 'solid'
13
13
  : cliOptions.tailwind;
14
+ let addOns = false;
15
+ let chosenAddOns = [];
16
+ if (Array.isArray(cliOptions.addOns)) {
17
+ addOns = true;
18
+ chosenAddOns = await finalizeAddOns(cliOptions.framework || DEFAULT_FRAMEWORK, cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER, cliOptions.addOns);
19
+ tailwind = true;
20
+ }
14
21
  return {
15
22
  framework: cliOptions.framework || 'react',
16
23
  projectName: cliOptions.projectName,
17
24
  typescript,
18
- tailwind: !!tailwind,
25
+ tailwind,
19
26
  packageManager: cliOptions.packageManager || DEFAULT_PACKAGE_MANAGER,
20
27
  mode: cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER,
21
28
  git: !!cliOptions.git,
22
- addOns: !!cliOptions.addOns,
23
- chosenAddOns: [],
29
+ addOns,
30
+ chosenAddOns,
24
31
  variableValues: {},
25
32
  };
26
33
  }
@@ -71,6 +78,9 @@ export async function promptForOptions(cliOptions) {
71
78
  options.typescript = true;
72
79
  options.tailwind = true;
73
80
  }
81
+ if (cliOptions.addOns) {
82
+ options.typescript = true;
83
+ }
74
84
  if (!cliOptions.projectName) {
75
85
  const value = await text({
76
86
  message: 'What would you like to name your project?',
@@ -172,51 +182,55 @@ export async function promptForOptions(cliOptions) {
172
182
  else {
173
183
  options.packageManager = cliOptions.packageManager;
174
184
  }
175
- // Select any add-ons
176
- const allAddOns = await getAllAddOns(options.framework, options.mode);
177
- const addOns = allAddOns.filter((addOn) => addOn.type === 'add-on');
178
- let selectedAddOns = [];
179
- if (options.typescript && cliOptions.addOns && addOns.length > 0) {
180
- const value = await multiselect({
181
- message: 'What add-ons would you like for your project:',
182
- options: addOns.map((addOn) => ({
183
- value: addOn.id,
184
- label: addOn.name,
185
- hint: addOn.description,
186
- })),
187
- required: false,
188
- });
189
- if (isCancel(value)) {
190
- cancel('Operation cancelled.');
191
- process.exit(0);
192
- }
193
- selectedAddOns = value;
194
- }
195
- // Select any examples
196
- const examples = allAddOns.filter((addOn) => addOn.type === 'example');
197
- let selectedExamples = [];
198
- if (options.typescript && cliOptions.addOns && examples.length > 0) {
199
- const value = await multiselect({
200
- message: 'Would you like any examples?',
201
- options: examples.map((addOn) => ({
202
- value: addOn.id,
203
- label: addOn.name,
204
- hint: addOn.description,
205
- })),
206
- required: false,
207
- });
208
- if (isCancel(value)) {
209
- cancel('Operation cancelled.');
210
- process.exit(0);
211
- }
212
- selectedExamples = value;
213
- }
214
- if (selectedAddOns.length > 0 || selectedExamples.length > 0) {
215
- options.chosenAddOns = await finalizeAddOns(options.framework, options.mode, [...selectedAddOns, ...selectedExamples]);
185
+ options.chosenAddOns = [];
186
+ if (Array.isArray(cliOptions.addOns)) {
187
+ options.chosenAddOns = await finalizeAddOns(options.framework, options.mode, cliOptions.addOns);
216
188
  options.tailwind = true;
217
189
  }
218
- else {
219
- options.chosenAddOns = [];
190
+ else if (cliOptions.addOns) {
191
+ // Select any add-ons
192
+ const allAddOns = await getAllAddOns(options.framework, options.mode);
193
+ const addOns = allAddOns.filter((addOn) => addOn.type === 'add-on');
194
+ let selectedAddOns = [];
195
+ if (options.typescript && addOns.length > 0) {
196
+ const value = await multiselect({
197
+ message: 'What add-ons would you like for your project:',
198
+ options: addOns.map((addOn) => ({
199
+ value: addOn.id,
200
+ label: addOn.name,
201
+ hint: addOn.description,
202
+ })),
203
+ required: false,
204
+ });
205
+ if (isCancel(value)) {
206
+ cancel('Operation cancelled.');
207
+ process.exit(0);
208
+ }
209
+ selectedAddOns = value;
210
+ }
211
+ // Select any examples
212
+ const examples = allAddOns.filter((addOn) => addOn.type === 'example');
213
+ let selectedExamples = [];
214
+ if (options.typescript && examples.length > 0) {
215
+ const value = await multiselect({
216
+ message: 'Would you like any examples?',
217
+ options: examples.map((addOn) => ({
218
+ value: addOn.id,
219
+ label: addOn.name,
220
+ hint: addOn.description,
221
+ })),
222
+ required: false,
223
+ });
224
+ if (isCancel(value)) {
225
+ cancel('Operation cancelled.');
226
+ process.exit(0);
227
+ }
228
+ selectedExamples = value;
229
+ }
230
+ if (selectedAddOns.length > 0 || selectedExamples.length > 0) {
231
+ options.chosenAddOns = await finalizeAddOns(options.framework, options.mode, [...selectedAddOns, ...selectedExamples]);
232
+ options.tailwind = true;
233
+ }
220
234
  }
221
235
  // Collect variables
222
236
  const variables = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-tsrouter-app",
3
- "version": "0.3.0-alpha.7",
3
+ "version": "0.3.0-alpha.9",
4
4
  "description": "Tanstack Application Builder",
5
5
  "bin": "./dist/index.js",
6
6
  "type": "module",
package/src/add-ons.ts CHANGED
@@ -2,8 +2,10 @@ import { readFile } from 'node:fs/promises'
2
2
  import { existsSync, readdirSync, statSync } from 'node:fs'
3
3
  import { resolve } from 'node:path'
4
4
  import { fileURLToPath } from 'node:url'
5
+ import chalk from 'chalk'
5
6
 
6
- import type { Framework } from './types.js'
7
+ import { DEFAULT_FRAMEWORK } from './constants.js'
8
+ import type { CliOptions, Framework } from './types.js'
7
9
 
8
10
  type BooleanVariable = {
9
11
  name: string
@@ -35,26 +37,10 @@ export type AddOn = {
35
37
  description: string
36
38
  link: string
37
39
  templates: Array<string>
38
- main?: Array<{
39
- imports: Array<string>
40
- initialize: Array<string>
41
- providers: Array<{
42
- open: string
43
- close: string
44
- }>
45
- }>
46
- layout?: {
47
- imports: Array<string>
48
- jsx: string
49
- }
50
40
  routes: Array<{
51
41
  url: string
52
42
  name: string
53
43
  }>
54
- userUi?: {
55
- import: string
56
- jsx: string
57
- }
58
44
  directory: string
59
45
  packageAdditions: {
60
46
  dependencies?: Record<string, string>
@@ -156,3 +142,15 @@ export async function finalizeAddOns(
156
142
 
157
143
  return [...finalAddOnIDs].map((id) => addOns.find((a) => a.id === id)!)
158
144
  }
145
+
146
+ export async function listAddOns(options: CliOptions) {
147
+ const mode =
148
+ options.template === 'file-router' ? 'file-router' : 'code-router'
149
+ const addOns = await getAllAddOns(
150
+ options.framework || DEFAULT_FRAMEWORK,
151
+ mode,
152
+ )
153
+ for (const addOn of addOns) {
154
+ console.log(`${chalk.bold(addOn.id)}: ${addOn.description}`)
155
+ }
156
+ }
package/src/cli.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  import { Command, InvalidArgumentError } from 'commander'
2
2
  import { intro, log } from '@clack/prompts'
3
+ import chalk from 'chalk'
3
4
 
4
5
  import { createApp } from './create-app.js'
5
6
  import { normalizeOptions, promptForOptions } from './options.js'
6
7
  import { SUPPORTED_PACKAGE_MANAGERS } from './package-manager.js'
7
8
 
9
+ import { getAllAddOns, listAddOns } from './add-ons.js'
8
10
  import { DEFAULT_FRAMEWORK, SUPPORTED_FRAMEWORKS } from './constants.js'
9
11
  import type { PackageManager } from './package-manager.js'
10
12
  import type { CliOptions, Framework } from './types.js'
@@ -63,26 +65,44 @@ export function cli() {
63
65
  },
64
66
  )
65
67
  .option('--tailwind', 'add Tailwind CSS', false)
66
- .option('--add-ons', 'pick from a list of available add-ons', false)
68
+ .option<Array<string> | boolean>(
69
+ '--add-ons [...add-ons]',
70
+ 'pick from a list of available add-ons (comma separated list)',
71
+ (value: string) => {
72
+ let addOns: Array<string> | boolean = !!value
73
+ if (typeof value === 'string') {
74
+ addOns = value.split(',').map((addon) => addon.trim())
75
+ }
76
+ return addOns
77
+ },
78
+ )
79
+ .option('--list-add-ons', 'list all available add-ons', false)
67
80
  .action(async (projectName: string, options: CliOptions) => {
68
- try {
69
- const cliOptions = {
70
- projectName,
71
- ...options,
72
- } as CliOptions
73
- let finalOptions = normalizeOptions(cliOptions)
74
- if (finalOptions) {
75
- intro(`Creating a new TanStack app in ${projectName}...`)
76
- } else {
77
- intro("Let's configure your TanStack application")
78
- finalOptions = await promptForOptions(cliOptions)
81
+ if (options.listAddOns) {
82
+ await listAddOns(options)
83
+ } else {
84
+ try {
85
+ const cliOptions = {
86
+ projectName,
87
+ ...options,
88
+ } as CliOptions
89
+
90
+ let finalOptions = await normalizeOptions(cliOptions)
91
+ if (finalOptions) {
92
+ intro(`Creating a new TanStack app in ${projectName}...`)
93
+ } else {
94
+ intro("Let's configure your TanStack application")
95
+ finalOptions = await promptForOptions(cliOptions)
96
+ }
97
+ await createApp(finalOptions)
98
+ } catch (error) {
99
+ log.error(
100
+ error instanceof Error
101
+ ? error.message
102
+ : 'An unknown error occurred',
103
+ )
104
+ process.exit(1)
79
105
  }
80
- await createApp(finalOptions)
81
- } catch (error) {
82
- log.error(
83
- error instanceof Error ? error.message : 'An unknown error occurred',
84
- )
85
- process.exit(1)
86
106
  }
87
107
  })
88
108