@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 +6 -6
- package/dist/add.js +2 -4
- package/dist/cli.js +41 -28
- package/dist/create-app.js +9 -3
- package/dist/custom-add-on.js +4 -4
- package/dist/file-helper.js +18 -0
- package/dist/mcp.js +128 -110
- package/dist/options.js +30 -11
- package/dist/types/add-ons.d.ts +5 -2
- package/dist/types/cli.d.ts +6 -1
- package/dist/types/create-app.d.ts +3 -1
- package/dist/types/file-helper.d.ts +2 -0
- package/dist/types/mcp.d.ts +7 -1
- package/dist/types/options.d.ts +5 -2
- package/dist/types/types.d.ts +2 -1
- package/package.json +1 -1
- package/src/add-ons.ts +18 -9
- package/src/add.ts +7 -5
- package/src/cli.ts +80 -54
- package/src/create-app.ts +15 -2
- package/src/custom-add-on.ts +9 -7
- package/src/file-helper.ts +20 -0
- package/src/mcp.ts +192 -142
- package/src/options.ts +57 -17
- package/src/types.ts +3 -1
- package/tsconfig.json +7 -1
package/dist/add-ons.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import { existsSync,
|
|
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] =
|
|
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
|
|
96
|
-
const addOns
|
|
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
|
|
36
|
-
|
|
37
|
-
.option('--
|
|
38
|
-
|
|
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('--
|
|
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('--
|
|
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
|
-
|
|
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
|
-
|
|
97
|
+
if (forcedMode) {
|
|
98
|
+
cliOptions.template = forcedMode;
|
|
99
|
+
}
|
|
100
|
+
let finalOptions = await normalizeOptions(cliOptions, forcedAddOns);
|
|
91
101
|
if (finalOptions) {
|
|
92
|
-
intro(`Creating a new
|
|
102
|
+
intro(`Creating a new ${appName} app in ${projectName}...`);
|
|
93
103
|
}
|
|
94
104
|
else {
|
|
95
|
-
intro(
|
|
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
|
}
|
package/dist/create-app.js
CHANGED
|
@@ -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
|
|
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}
|
package/dist/custom-add-on.js
CHANGED
|
@@ -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)] = (
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
});
|
|
142
|
-
server.tool('
|
|
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:
|
|
88
|
+
content: [{ type: 'text', text: JSON.stringify(tanStackReactAddOns) }],
|
|
179
89
|
};
|
|
180
|
-
}
|
|
181
|
-
|
|
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
|
-
|
|
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) ||
|
|
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 = [
|
|
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
|
|
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
|
|
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 ||
|
|
281
|
-
|
|
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) {
|
package/dist/types/add-ons.d.ts
CHANGED
|
@@ -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
|
|
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>;
|
package/dist/types/cli.d.ts
CHANGED