@tanstack/cta-engine 0.10.0-alpha.6 → 0.11.0

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/add-ons.js CHANGED
@@ -1,9 +1,10 @@
1
1
  import { readFile } from 'node:fs/promises';
2
- import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
2
+ import { existsSync, readdirSync, statSync } from 'node:fs';
3
3
  import { resolve } from 'node:path';
4
4
  import chalk from 'chalk';
5
5
  import { getTemplatesRoot } from './templates.js';
6
6
  import { DEFAULT_FRAMEWORK } from './constants.js';
7
+ import { readFileHelper } from './file-helper.js';
7
8
  function isDirectory(path) {
8
9
  return statSync(path).isDirectory();
9
10
  }
@@ -15,7 +16,7 @@ function findFilesRecursively(path, files) {
15
16
  findFilesRecursively(filePath, files);
16
17
  }
17
18
  else {
18
- files[filePath] = readFileSync(filePath, 'utf-8').toString();
19
+ files[filePath] = readFileHelper(filePath);
19
20
  }
20
21
  }
21
22
  }
@@ -91,10 +92,9 @@ export async function finalizeAddOns(framework, template, chosenAddOnIDs) {
91
92
  const finalAddOns = [...finalAddOnIDs].map((id) => addOns.find((a) => a.id === id));
92
93
  return finalAddOns;
93
94
  }
94
- export async function listAddOns(options) {
95
- const mode = options.template === 'file-router' ? 'file-router' : 'code-router';
96
- const addOns = await getAllAddOns(options.framework || DEFAULT_FRAMEWORK, mode);
97
- for (const addOn of addOns) {
95
+ export async function listAddOns(options, { forcedMode, forcedAddOns = [], }) {
96
+ const addOns = await getAllAddOns(options.framework || DEFAULT_FRAMEWORK, forcedMode || options.template || 'typescript');
97
+ for (const addOn of addOns.filter((a) => !forcedAddOns.includes(a.id))) {
98
98
  console.log(`${chalk.bold(addOn.id)}: ${addOn.description}`);
99
99
  }
100
100
  }
package/dist/add.js CHANGED
@@ -21,10 +21,7 @@ async function createOptions(json, addOns) {
21
21
  return {
22
22
  ...json,
23
23
  tailwind: true,
24
- chosenAddOns: await finalizeAddOns(json.framework, json.mode, [
25
- ...json.existingAddOns,
26
- ...addOns,
27
- ]),
24
+ chosenAddOns: await finalizeAddOns(json.framework, json.mode, [...json.existingAddOns, ...addOns]),
28
25
  };
29
26
  }
30
27
  async function runCreateApp(options) {
@@ -33,6 +30,7 @@ async function runCreateApp(options) {
33
30
  silent: true,
34
31
  environment,
35
32
  cwd: process.cwd(),
33
+ name: 'create-tsrouter-app',
36
34
  });
37
35
  return output;
38
36
  }
package/dist/cli.js CHANGED
@@ -9,11 +9,9 @@ import { listAddOns } from './add-ons.js';
9
9
  import { DEFAULT_FRAMEWORK, SUPPORTED_FRAMEWORKS } from './constants.js';
10
10
  // import { initAddOn } from './custom-add-on.js'
11
11
  import { createDefaultEnvironment } from './environment.js';
12
- export function cli() {
12
+ export function cli({ name, appName, forcedMode, forcedAddOns, }) {
13
13
  const program = new Command();
14
- program
15
- .name('create-tsrouter-app')
16
- .description('CLI to create a new TanStack application');
14
+ program.name(name).description(`CLI to create a new ${appName} application`);
17
15
  // program
18
16
  // .command('add')
19
17
  // .argument('add-on', 'Name of the add-on (or add-ons separated by commas)')
@@ -32,24 +30,25 @@ export function cli() {
32
30
  // .action(async () => {
33
31
  // await initAddOn('overlay')
34
32
  // })
35
- program // 104 22
36
- .argument('[project-name]', 'name of the project')
37
- .option('--no-git', 'do not create a git repository')
38
- .option('--target-dir <path>', 'the directory to create the project in')
33
+ program.argument('[project-name]', 'name of the project');
34
+ if (!forcedMode) {
35
+ program.option('--template <type>', 'project template (typescript, javascript, file-router)', (value) => {
36
+ if (value !== 'typescript' &&
37
+ value !== 'javascript' &&
38
+ value !== 'file-router') {
39
+ throw new InvalidArgumentError(`Invalid template: ${value}. Only the following are allowed: typescript, javascript, file-router`);
40
+ }
41
+ return value;
42
+ });
43
+ }
44
+ program
39
45
  .option('--framework <type>', 'project framework (solid, react)', (value) => {
40
46
  if (!SUPPORTED_FRAMEWORKS.includes(value)) {
41
47
  throw new InvalidArgumentError(`Invalid framework: ${value}. Only the following are allowed: ${SUPPORTED_FRAMEWORKS.join(', ')}`);
42
48
  }
43
49
  return value;
44
50
  }, DEFAULT_FRAMEWORK)
45
- .option('--template <type>', 'project template (typescript, javascript, file-router)', (value) => {
46
- if (value !== 'typescript' &&
47
- value !== 'javascript' &&
48
- value !== 'file-router') {
49
- throw new InvalidArgumentError(`Invalid template: ${value}. Only the following are allowed: typescript, javascript, file-router`);
50
- }
51
- return value;
52
- })
51
+ // .option('--overlay [url]', 'add an overlay from a URL', false)
53
52
  .option(`--package-manager <${SUPPORTED_PACKAGE_MANAGERS.join('|')}>`, `Explicitly tell the CLI to use this package manager`, (value) => {
54
53
  if (!SUPPORTED_PACKAGE_MANAGERS.includes(value)) {
55
54
  throw new InvalidArgumentError(`Invalid package manager: ${value}. The following are allowed: ${SUPPORTED_PACKAGE_MANAGERS.join(', ')}`);
@@ -71,15 +70,23 @@ export function cli() {
71
70
  return addOns;
72
71
  })
73
72
  .option('--list-add-ons', 'list all available add-ons', false)
74
- .option('--overlay [url]', 'add an overlay from a URL', false)
73
+ .option('--no-git', 'do not create a git repository')
74
+ .option('--target-dir <path>', 'the directory to create the project in')
75
75
  .option('--mcp', 'run the MCP server', false)
76
- .option('--mcp-sse', 'run the MCP server in SSE mode', false)
77
- .action(async (projectName, options) => {
76
+ .option('--mcp-sse', 'run the MCP server in SSE mode', false);
77
+ program.action(async (projectName, options) => {
78
78
  if (options.listAddOns) {
79
- await listAddOns(options);
79
+ await listAddOns(options, {
80
+ forcedMode,
81
+ forcedAddOns,
82
+ });
80
83
  }
81
84
  else if (options.mcp || options.mcpSse) {
82
- await runServer(!!options.mcpSse);
85
+ await runServer(!!options.mcpSse, {
86
+ forcedMode,
87
+ forcedAddOns,
88
+ appName,
89
+ });
83
90
  }
84
91
  else {
85
92
  try {
@@ -87,23 +94,29 @@ export function cli() {
87
94
  projectName,
88
95
  ...options,
89
96
  };
90
- let finalOptions = await normalizeOptions(cliOptions);
97
+ if (forcedMode) {
98
+ cliOptions.template = forcedMode;
99
+ }
100
+ let finalOptions = await normalizeOptions(cliOptions, forcedAddOns);
91
101
  if (finalOptions) {
92
- intro(`Creating a new TanStack app in ${projectName}...`);
102
+ intro(`Creating a new ${appName} app in ${projectName}...`);
93
103
  }
94
104
  else {
95
- intro("Let's configure your TanStack application");
96
- finalOptions = await promptForOptions(cliOptions);
105
+ intro(`Let's configure your ${appName} application`);
106
+ finalOptions = await promptForOptions(cliOptions, {
107
+ forcedMode,
108
+ forcedAddOns,
109
+ });
97
110
  }
98
111
  await createApp(finalOptions, {
99
112
  environment: createDefaultEnvironment(),
100
113
  cwd: options.targetDir || undefined,
114
+ name,
115
+ appName,
101
116
  });
102
117
  }
103
118
  catch (error) {
104
- log.error(error instanceof Error
105
- ? error.message
106
- : 'An unknown error occurred');
119
+ log.error(error instanceof Error ? error.message : 'An unknown error occurred');
107
120
  process.exit(1);
108
121
  }
109
122
  }
@@ -8,6 +8,7 @@ import { CODE_ROUTER, FILE_ROUTER } from './constants.js';
8
8
  import { sortObject } from './utils.js';
9
9
  import { writeConfigFile } from './config-file.js';
10
10
  import { packageManagerExecute } from './package-manager.js';
11
+ import { getBinaryFile } from './file-helper.js';
11
12
  function createCopyFiles(environment, targetDir) {
12
13
  return async function copyFiles(templateDir, files,
13
14
  // optionally copy files from a folder to the root
@@ -197,6 +198,11 @@ async function copyAddOnFile(environment, content, target, targetPath, templateF
197
198
  isAppend = true;
198
199
  }
199
200
  const finalTargetPath = resolve(dirname(targetPath), targetFile);
201
+ const binaryContent = getBinaryFile(content);
202
+ if (binaryContent) {
203
+ await environment.writeFile(finalTargetPath, binaryContent);
204
+ return;
205
+ }
200
206
  if (isTemplate) {
201
207
  await templateFile(content, finalTargetPath);
202
208
  }
@@ -209,7 +215,7 @@ async function copyAddOnFile(environment, content, target, targetPath, templateF
209
215
  }
210
216
  }
211
217
  }
212
- export async function createApp(options, { silent = false, environment, cwd, }) {
218
+ export async function createApp(options, { silent = false, environment, cwd, appName = 'TanStack', }) {
213
219
  environment.startRun();
214
220
  const templateDirBase = resolve(getTemplatesRoot(), options.framework, 'base');
215
221
  const templateDirRouter = resolve(getTemplatesRoot(), options.framework, options.mode);
@@ -309,7 +315,7 @@ export async function createApp(options, { silent = false, environment, cwd, })
309
315
  // Copy all the asset files from the addons
310
316
  const s = silent ? null : spinner();
311
317
  for (const type of ['add-on', 'example']) {
312
- for (const phase of ['setup', 'add-on']) {
318
+ for (const phase of ['setup', 'add-on', 'example']) {
313
319
  for (const addOn of options.chosenAddOns.filter((addOn) => addOn.phase === phase && addOn.type === type)) {
314
320
  s?.start(`Setting up ${addOn.name}...`);
315
321
  await runAddOn(addOn);
@@ -480,7 +486,7 @@ ${environment.getErrors().join('\n')}`;
480
486
  if (options.packageManager === 'deno') {
481
487
  startCommand = `deno ${isAddOnEnabled('start') ? 'task dev' : 'start'}`;
482
488
  }
483
- outro(`Your TanStack app is ready in '${basename(targetDir)}'.
489
+ outro(`Your ${appName} app is ready in '${basename(targetDir)}'.
484
490
 
485
491
  Use the following commands to start your app:
486
492
  % cd ${options.projectName}
@@ -6,6 +6,7 @@ import { createMemoryEnvironment } from './environment.js';
6
6
  import { createApp } from './create-app.js';
7
7
  import { readConfigFile } from './config-file.js';
8
8
  import { finalizeAddOns } from './add-ons.js';
9
+ import { readFileHelper } from './file-helper.js';
9
10
  const INFO_FILE = {
10
11
  'add-on': '.add-on/info.json',
11
12
  overlay: 'overlay-info.json',
@@ -78,9 +79,7 @@ export default (parentRoute: RootRoute) => createRoute({
78
79
  async function createOptions(json) {
79
80
  return {
80
81
  ...json,
81
- chosenAddOns: await finalizeAddOns(json.framework, json.mode, [
82
- ...json.existingAddOns,
83
- ]),
82
+ chosenAddOns: await finalizeAddOns(json.framework, json.mode, [...json.existingAddOns]),
84
83
  };
85
84
  }
86
85
  async function runCreateApp(options) {
@@ -89,6 +88,7 @@ async function runCreateApp(options) {
89
88
  silent: true,
90
89
  environment,
91
90
  cwd: process.cwd(),
91
+ name: 'create-tsrouter-app',
92
92
  });
93
93
  return output;
94
94
  }
@@ -99,7 +99,7 @@ async function recursivelyGatherFiles(path, files) {
99
99
  await recursivelyGatherFiles(resolve(path, file.name), files);
100
100
  }
101
101
  else {
102
- files[resolve(path, file.name)] = (await readFile(resolve(path, file.name))).toString();
102
+ files[resolve(path, file.name)] = readFileHelper(resolve(path, file.name));
103
103
  }
104
104
  }
105
105
  }
@@ -0,0 +1,18 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { extname } from 'node:path';
3
+ const BINARY_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico'];
4
+ export function readFileHelper(path) {
5
+ if (BINARY_EXTENSIONS.includes(extname(path))) {
6
+ return `base64::${readFileSync(path).toString('base64')}`;
7
+ }
8
+ else {
9
+ return readFileSync(path, 'utf-8').toString();
10
+ }
11
+ }
12
+ export function getBinaryFile(content) {
13
+ if (content.startsWith('base64::')) {
14
+ const binaryContent = Buffer.from(content.replace('base64::', ''), 'base64');
15
+ return binaryContent;
16
+ }
17
+ return null;
18
+ }
package/dist/mcp.js CHANGED
@@ -6,10 +6,6 @@ import { z } from 'zod';
6
6
  import { createApp } from './create-app.js';
7
7
  import { finalizeAddOns } from './add-ons.js';
8
8
  import { createDefaultEnvironment } from './environment.js';
9
- const server = new McpServer({
10
- name: 'Demo',
11
- version: '1.0.0',
12
- });
13
9
  const tanStackReactAddOns = [
14
10
  {
15
11
  id: 'clerk',
@@ -52,62 +48,6 @@ const tanStackReactAddOns = [
52
48
  description: 'Add an AI chatbot example to the application',
53
49
  },
54
50
  ];
55
- server.tool('listTanStackReactAddOns', {}, () => {
56
- return {
57
- content: [{ type: 'text', text: JSON.stringify(tanStackReactAddOns) }],
58
- };
59
- });
60
- server.tool('createTanStackReactApplication', {
61
- projectName: z
62
- .string()
63
- .describe('The package.json module name of the application (will also be the directory name)'),
64
- cwd: z.string().describe('The directory to create the application in'),
65
- addOns: z
66
- .array(z.enum([
67
- 'clerk',
68
- 'convex',
69
- 'form',
70
- 'netlify',
71
- 'sentry',
72
- 'shadcn',
73
- 'start',
74
- 'store',
75
- 'tanstack-query',
76
- 'tanchat',
77
- ]))
78
- .describe('The IDs of the add-ons to install'),
79
- }, async ({ projectName, addOns, cwd }) => {
80
- try {
81
- process.chdir(cwd);
82
- const chosenAddOns = await finalizeAddOns('react', 'file-router', addOns);
83
- await createApp({
84
- projectName: projectName.replace(/^\//, './'),
85
- framework: 'react',
86
- typescript: true,
87
- tailwind: true,
88
- packageManager: 'pnpm',
89
- toolchain: 'none',
90
- mode: 'file-router',
91
- addOns: true,
92
- chosenAddOns,
93
- git: true,
94
- variableValues: {},
95
- }, {
96
- silent: true,
97
- environment: createDefaultEnvironment(),
98
- });
99
- return {
100
- content: [{ type: 'text', text: 'Application created successfully' }],
101
- };
102
- }
103
- catch (error) {
104
- return {
105
- content: [
106
- { type: 'text', text: `Error creating application: ${error}` },
107
- ],
108
- };
109
- }
110
- });
111
51
  const tanStackSolidAddOns = [
112
52
  {
113
53
  id: 'solid-ui',
@@ -125,6 +65,10 @@ const tanStackSolidAddOns = [
125
65
  id: 'store',
126
66
  description: 'Enable the TanStack Store state management library',
127
67
  },
68
+ {
69
+ id: 'start',
70
+ description: 'Set this if you want a TanStack Start application that supports server functions or APIs',
71
+ },
128
72
  {
129
73
  id: 'tanstack-query',
130
74
  description: 'Enable TanStack Query for data fetching',
@@ -134,62 +78,136 @@ const tanStackSolidAddOns = [
134
78
  description: 'Add an AI chatbot example to the application',
135
79
  },
136
80
  ];
137
- server.tool('listTanStackSolidAddOns', {}, () => {
138
- return {
139
- content: [{ type: 'text', text: JSON.stringify(tanStackSolidAddOns) }],
140
- };
141
- });
142
- server.tool('createTanStackSolidApplication', {
143
- projectName: z
144
- .string()
145
- .describe('The package.json module name of the application (will also be the directory name)'),
146
- cwd: z.string().describe('The directory to create the application in'),
147
- addOns: z
148
- .array(z.enum([
149
- 'solid-ui',
150
- 'form',
151
- 'sentry',
152
- 'store',
153
- 'tanstack-query',
154
- 'tanchat',
155
- ]))
156
- .describe('The IDs of the add-ons to install'),
157
- }, async ({ projectName, addOns, cwd }) => {
158
- try {
159
- process.chdir(cwd);
160
- const chosenAddOns = await finalizeAddOns('solid', 'file-router', addOns);
161
- await createApp({
162
- projectName: projectName.replace(/^\//, './'),
163
- framework: 'solid',
164
- typescript: true,
165
- tailwind: true,
166
- packageManager: 'pnpm',
167
- toolchain: 'none',
168
- mode: 'file-router',
169
- addOns: true,
170
- chosenAddOns,
171
- git: true,
172
- variableValues: {},
173
- }, {
174
- silent: true,
175
- environment: createDefaultEnvironment(),
176
- });
81
+ function createServer({ appName, forcedAddOns = [], name, }) {
82
+ const server = new McpServer({
83
+ name: `${appName} Application Builder`,
84
+ version: '1.0.0',
85
+ });
86
+ server.tool('listTanStackReactAddOns', {}, () => {
177
87
  return {
178
- content: [{ type: 'text', text: 'Application created successfully' }],
88
+ content: [{ type: 'text', text: JSON.stringify(tanStackReactAddOns) }],
179
89
  };
180
- }
181
- catch (error) {
90
+ });
91
+ server.tool('createTanStackReactApplication', {
92
+ projectName: z
93
+ .string()
94
+ .describe('The package.json module name of the application (will also be the directory name)'),
95
+ cwd: z.string().describe('The directory to create the application in'),
96
+ addOns: z
97
+ .array(z.enum([
98
+ 'clerk',
99
+ 'convex',
100
+ 'form',
101
+ 'netlify',
102
+ 'sentry',
103
+ 'shadcn',
104
+ 'start',
105
+ 'store',
106
+ 'tanstack-query',
107
+ 'tanchat',
108
+ ]))
109
+ .describe('The IDs of the add-ons to install'),
110
+ targetDir: z
111
+ .string()
112
+ .describe('The directory to create the application in. Use the absolute path of the directory you want the application to be created in'),
113
+ }, async ({ projectName, addOns, cwd, targetDir }) => {
114
+ try {
115
+ process.chdir(cwd);
116
+ const chosenAddOns = await finalizeAddOns('react', 'file-router', Array.from(new Set([...addOns, ...forcedAddOns])));
117
+ await createApp({
118
+ projectName: projectName.replace(/^\//, './'),
119
+ framework: 'react',
120
+ typescript: true,
121
+ tailwind: true,
122
+ packageManager: 'pnpm',
123
+ toolchain: 'none',
124
+ mode: 'file-router',
125
+ addOns: true,
126
+ chosenAddOns,
127
+ git: true,
128
+ variableValues: {},
129
+ }, {
130
+ silent: true,
131
+ environment: createDefaultEnvironment(),
132
+ name,
133
+ cwd: targetDir,
134
+ });
135
+ return {
136
+ content: [{ type: 'text', text: 'Application created successfully' }],
137
+ };
138
+ }
139
+ catch (error) {
140
+ return {
141
+ content: [
142
+ { type: 'text', text: `Error creating application: ${error}` },
143
+ ],
144
+ };
145
+ }
146
+ });
147
+ server.tool('listTanStackSolidAddOns', {}, () => {
182
148
  return {
183
- content: [
184
- { type: 'text', text: `Error creating application: ${error}` },
185
- ],
149
+ content: [{ type: 'text', text: JSON.stringify(tanStackSolidAddOns) }],
186
150
  };
187
- }
188
- });
189
- export default async function runServer(sse) {
151
+ });
152
+ server.tool('createTanStackSolidApplication', {
153
+ projectName: z
154
+ .string()
155
+ .describe('The package.json module name of the application (will also be the directory name)'),
156
+ cwd: z.string().describe('The directory to create the application in'),
157
+ addOns: z
158
+ .array(z.enum([
159
+ 'solid-ui',
160
+ 'form',
161
+ 'sentry',
162
+ 'store',
163
+ 'tanstack-query',
164
+ 'tanchat',
165
+ ]))
166
+ .describe('The IDs of the add-ons to install'),
167
+ targetDir: z
168
+ .string()
169
+ .describe('The directory to create the application in. Use the absolute path of the directory you want the application to be created in'),
170
+ }, async ({ projectName, addOns, cwd, targetDir }) => {
171
+ try {
172
+ process.chdir(cwd);
173
+ const chosenAddOns = await finalizeAddOns('solid', 'file-router', Array.from(new Set([...addOns, ...forcedAddOns])));
174
+ await createApp({
175
+ projectName: projectName.replace(/^\//, './'),
176
+ framework: 'solid',
177
+ typescript: true,
178
+ tailwind: true,
179
+ packageManager: 'pnpm',
180
+ toolchain: 'none',
181
+ mode: 'file-router',
182
+ addOns: true,
183
+ chosenAddOns,
184
+ git: true,
185
+ variableValues: {},
186
+ }, {
187
+ silent: true,
188
+ environment: createDefaultEnvironment(),
189
+ name,
190
+ cwd: targetDir,
191
+ });
192
+ return {
193
+ content: [{ type: 'text', text: 'Application created successfully' }],
194
+ };
195
+ }
196
+ catch (error) {
197
+ return {
198
+ content: [
199
+ { type: 'text', text: `Error creating application: ${error}` },
200
+ ],
201
+ };
202
+ }
203
+ });
204
+ return server;
205
+ }
206
+ export default async function runServer(sse, { forcedAddOns, appName, name, }) {
207
+ let transport = null;
208
+ const server = createServer({ appName, forcedAddOns, name });
190
209
  if (sse) {
191
210
  const app = express();
192
- let transport = null;
193
211
  app.get('/sse', (req, res) => {
194
212
  transport = new SSEServerTransport('/messages', res);
195
213
  server.connect(transport);
package/dist/options.js CHANGED
@@ -4,7 +4,7 @@ import { DEFAULT_TOOLCHAIN, SUPPORTED_TOOLCHAINS } from './toolchain.js';
4
4
  import { CODE_ROUTER, DEFAULT_FRAMEWORK, FILE_ROUTER } from './constants.js';
5
5
  import { finalizeAddOns, getAllAddOns, loadRemoteAddOn } from './add-ons.js';
6
6
  // If all CLI options are provided, use them directly
7
- export async function normalizeOptions(cliOptions) {
7
+ export async function normalizeOptions(cliOptions, forcedAddOns) {
8
8
  // in some cases, if you use windows/powershell, the argument for addons
9
9
  // if sepparated by comma is not really passed as an array, but as a string
10
10
  // with spaces, We need to normalize this edge case.
@@ -34,11 +34,17 @@ export async function normalizeOptions(cliOptions) {
34
34
  }
35
35
  let addOns = false;
36
36
  let chosenAddOns = [];
37
- if (Array.isArray(cliOptions.addOns) || overlay?.dependsOn) {
37
+ if (Array.isArray(cliOptions.addOns) ||
38
+ overlay?.dependsOn ||
39
+ forcedAddOns) {
38
40
  addOns = true;
39
- let finalAddOns = [...(overlay?.dependsOn || [])];
41
+ let finalAddOns = Array.from(new Set([...(overlay?.dependsOn || []), ...(forcedAddOns || [])]));
40
42
  if (cliOptions.addOns && Array.isArray(cliOptions.addOns)) {
41
- finalAddOns = [...finalAddOns, ...cliOptions.addOns];
43
+ finalAddOns = Array.from(new Set([
44
+ ...(forcedAddOns || []),
45
+ ...finalAddOns,
46
+ ...cliOptions.addOns,
47
+ ]));
42
48
  }
43
49
  chosenAddOns = await finalizeAddOns(cliOptions.framework || DEFAULT_FRAMEWORK, cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER, finalAddOns);
44
50
  tailwind = true;
@@ -101,7 +107,7 @@ async function collectVariables(variables) {
101
107
  }
102
108
  return responses;
103
109
  }
104
- export async function promptForOptions(cliOptions) {
110
+ export async function promptForOptions(cliOptions, { forcedAddOns = [], forcedMode, }) {
105
111
  const options = {};
106
112
  options.framework = cliOptions.framework || DEFAULT_FRAMEWORK;
107
113
  if (options.framework === 'solid') {
@@ -128,7 +134,7 @@ export async function promptForOptions(cliOptions) {
128
134
  options.projectName = value;
129
135
  }
130
136
  // Router type selection
131
- if (!cliOptions.template) {
137
+ if (!cliOptions.template && !forcedMode) {
132
138
  const routerType = await select({
133
139
  message: 'Select the router type:',
134
140
  options: [
@@ -149,6 +155,10 @@ export async function promptForOptions(cliOptions) {
149
155
  }
150
156
  options.mode = routerType;
151
157
  }
158
+ else if (forcedMode) {
159
+ options.mode = forcedMode === 'file-router' ? FILE_ROUTER : CODE_ROUTER;
160
+ options.typescript = options.mode === FILE_ROUTER;
161
+ }
152
162
  else {
153
163
  options.mode =
154
164
  cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER;
@@ -234,7 +244,7 @@ export async function promptForOptions(cliOptions) {
234
244
  }
235
245
  options.chosenAddOns = [];
236
246
  if (Array.isArray(cliOptions.addOns)) {
237
- options.chosenAddOns = await finalizeAddOns(options.framework, options.mode, cliOptions.addOns);
247
+ options.chosenAddOns = await finalizeAddOns(options.framework, options.mode, Array.from(new Set([...cliOptions.addOns, ...forcedAddOns])));
238
248
  options.tailwind = true;
239
249
  }
240
250
  else if (cliOptions.addOns) {
@@ -245,7 +255,9 @@ export async function promptForOptions(cliOptions) {
245
255
  if (options.typescript && addOns.length > 0) {
246
256
  const value = await multiselect({
247
257
  message: 'What add-ons would you like for your project:',
248
- options: addOns.map((addOn) => ({
258
+ options: addOns
259
+ .filter((addOn) => !forcedAddOns.includes(addOn.id))
260
+ .map((addOn) => ({
249
261
  value: addOn.id,
250
262
  label: addOn.name,
251
263
  hint: addOn.description,
@@ -264,7 +276,9 @@ export async function promptForOptions(cliOptions) {
264
276
  if (options.typescript && examples.length > 0) {
265
277
  const value = await multiselect({
266
278
  message: 'Would you like any examples?',
267
- options: examples.map((addOn) => ({
279
+ options: examples
280
+ .filter((addOn) => !forcedAddOns.includes(addOn.id))
281
+ .map((addOn) => ({
268
282
  value: addOn.id,
269
283
  label: addOn.name,
270
284
  hint: addOn.description,
@@ -277,11 +291,16 @@ export async function promptForOptions(cliOptions) {
277
291
  }
278
292
  selectedExamples = value;
279
293
  }
280
- if (selectedAddOns.length > 0 || selectedExamples.length > 0) {
281
- options.chosenAddOns = await finalizeAddOns(options.framework, options.mode, [...selectedAddOns, ...selectedExamples]);
294
+ if (selectedAddOns.length > 0 ||
295
+ selectedExamples.length > 0 ||
296
+ forcedAddOns.length > 0) {
297
+ options.chosenAddOns = await finalizeAddOns(options.framework, options.mode, Array.from(new Set([...selectedAddOns, ...selectedExamples, ...forcedAddOns])));
282
298
  options.tailwind = true;
283
299
  }
284
300
  }
301
+ else if (forcedAddOns.length > 0) {
302
+ options.chosenAddOns = await finalizeAddOns(options.framework, options.mode, forcedAddOns);
303
+ }
285
304
  // Collect variables
286
305
  const variables = [];
287
306
  for (const addOn of options.chosenAddOns) {
@@ -1,5 +1,8 @@
1
- import type { AddOn, CliOptions, Framework } from './types.js';
1
+ import type { AddOn, CliOptions, Framework, TemplateOptions } from './types.js';
2
2
  export declare function getAllAddOns(framework: Framework, template: string): Promise<Array<AddOn>>;
3
3
  export declare function finalizeAddOns(framework: Framework, template: string, chosenAddOnIDs: Array<string>): Promise<Array<AddOn>>;
4
- export declare function listAddOns(options: CliOptions): Promise<void>;
4
+ export declare function listAddOns(options: CliOptions, { forcedMode, forcedAddOns, }: {
5
+ forcedMode?: TemplateOptions;
6
+ forcedAddOns?: Array<string>;
7
+ }): Promise<void>;
5
8
  export declare function loadRemoteAddOn(url: string): Promise<AddOn>;
@@ -1 +1,6 @@
1
- export declare function cli(): void;
1
+ export declare function cli({ name, appName, forcedMode, forcedAddOns, }: {
2
+ name: string;
3
+ appName: string;
4
+ forcedMode?: 'typescript' | 'javascript' | 'file-router';
5
+ forcedAddOns?: Array<string>;
6
+ }): void;