create-tsrouter-app 0.5.0 → 0.6.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/README.md +8 -0
- package/dist/cli.js +7 -0
- package/dist/create-app.js +52 -4
- package/dist/mcp.js +10 -0
- package/dist/options.js +22 -1
- package/dist/toolchain.js +2 -0
- package/package.json +1 -1
- package/src/cli.ts +16 -0
- package/src/create-app.ts +65 -7
- package/src/mcp.ts +10 -0
- package/src/options.ts +22 -1
- package/src/toolchain.ts +3 -0
- package/src/types.ts +3 -0
- package/templates/react/base/README.md.ejs +10 -0
- package/templates/react/base/_dot_vscode/settings.biome.json +38 -0
- package/templates/react/base/package.biome.json +10 -0
- package/templates/react/base/toolchain/biome.json +31 -0
- package/templates/react/code-router/src/main.tsx.ejs +2 -2
- package/templates/react/file-router/src/main.tsx.ejs +2 -2
- package/templates/solid/base/_dot_vscode/settings.biome.json +38 -0
- package/templates/solid/base/package.biome.json +10 -0
- package/templates/solid/base/toolchain/biome.json +31 -0
- package/templates/solid/code-router/src/main.tsx.ejs +4 -2
- package/templates/solid/file-router/src/main.tsx.ejs +4 -2
- package/templates/solid/file-router/src/routes/__root.tsx.ejs +1 -1
package/README.md
CHANGED
|
@@ -30,6 +30,7 @@ This will start an interactive CLI that guides you through the setup process, al
|
|
|
30
30
|
- TypeScript support
|
|
31
31
|
- Tailwind CSS integration
|
|
32
32
|
- Package manager
|
|
33
|
+
- Toolchain
|
|
33
34
|
- Git initialization
|
|
34
35
|
|
|
35
36
|
## Command Line Options
|
|
@@ -45,6 +46,7 @@ Available options:
|
|
|
45
46
|
- `--template <type>`: Choose between `file-router`, `typescript`, or `javascript`
|
|
46
47
|
- `--tailwind`: Enable Tailwind CSS
|
|
47
48
|
- `--package-manager`: Specify your preferred package manager (`npm`, `yarn`, `pnpm`, `bun`, or `deno`)
|
|
49
|
+
- `--toolchain`: Specify your toolchain solution for formatting/linting (`biome`)
|
|
48
50
|
- `--no-git`: Do not initialize a git repository
|
|
49
51
|
- `--add-ons`: Enable add-on selection or specify add-ons to install
|
|
50
52
|
|
|
@@ -94,6 +96,12 @@ Choose your preferred package manager (`npm`, `bun`, `yarn`, `pnpm`, or `deno`)
|
|
|
94
96
|
|
|
95
97
|
Extensive documentation on using the TanStack Router, migrating to a File Base Routing approach, as well as integrating [@tanstack/react-query](https://tanstack.com/query/latest) and [@tanstack/store](https://tanstack.com/store/latest) can be found in the generated `README.md` for your project.
|
|
96
98
|
|
|
99
|
+
### Toolchain
|
|
100
|
+
|
|
101
|
+
Choose your preferred solution for formatting and linting either through the interactive CLI or using the `--toolchain` flag.
|
|
102
|
+
|
|
103
|
+
Setting this flag to `biome` will configure it as your toolchain of choice, adding a `biome.json` to the root of the project. Consult the [biome documentation](https://biomejs.dev/guides/getting-started/) for further customization.
|
|
104
|
+
|
|
97
105
|
## Add-ons (experimental)
|
|
98
106
|
|
|
99
107
|
You can enable add-on selection:
|
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 { SUPPORTED_TOOLCHAINS } from './toolchain.js';
|
|
6
7
|
import runServer from './mcp.js';
|
|
7
8
|
import { listAddOns } from './add-ons.js';
|
|
8
9
|
import { DEFAULT_FRAMEWORK, SUPPORTED_FRAMEWORKS } from './constants.js';
|
|
@@ -32,6 +33,12 @@ export function cli() {
|
|
|
32
33
|
throw new InvalidArgumentError(`Invalid package manager: ${value}. The following are allowed: ${SUPPORTED_PACKAGE_MANAGERS.join(', ')}`);
|
|
33
34
|
}
|
|
34
35
|
return value;
|
|
36
|
+
})
|
|
37
|
+
.option(`--toolchain <${SUPPORTED_TOOLCHAINS.join('|')}>`, `Explicitly tell the CLI to use this toolchain`, (value) => {
|
|
38
|
+
if (!SUPPORTED_TOOLCHAINS.includes(value)) {
|
|
39
|
+
throw new InvalidArgumentError(`Invalid toolchain: ${value}. The following are allowed: ${SUPPORTED_TOOLCHAINS.join(', ')}`);
|
|
40
|
+
}
|
|
41
|
+
return value;
|
|
35
42
|
})
|
|
36
43
|
.option('--tailwind', 'add Tailwind CSS', false)
|
|
37
44
|
.option('--add-ons [...add-ons]', 'pick from a list of available add-ons (comma separated list)', (value) => {
|
package/dist/create-app.js
CHANGED
|
@@ -18,9 +18,15 @@ function sortObject(obj) {
|
|
|
18
18
|
}, {});
|
|
19
19
|
}
|
|
20
20
|
function createCopyFiles(targetDir) {
|
|
21
|
-
return async function copyFiles(templateDir, files
|
|
21
|
+
return async function copyFiles(templateDir, files,
|
|
22
|
+
// optionally copy files from a folder to the root
|
|
23
|
+
toRoot) {
|
|
22
24
|
for (const file of files) {
|
|
23
|
-
|
|
25
|
+
let targetFileName = file.replace('.tw', '');
|
|
26
|
+
if (toRoot) {
|
|
27
|
+
const fileNoPath = targetFileName.split('/').pop();
|
|
28
|
+
targetFileName = fileNoPath ? `./${fileNoPath}` : targetFileName;
|
|
29
|
+
}
|
|
24
30
|
await copyFile(resolve(templateDir, file), resolve(targetDir, targetFileName));
|
|
25
31
|
}
|
|
26
32
|
};
|
|
@@ -38,6 +44,7 @@ function createTemplateFile(projectName, options, targetDir) {
|
|
|
38
44
|
projectName: projectName,
|
|
39
45
|
typescript: options.typescript,
|
|
40
46
|
tailwind: options.tailwind,
|
|
47
|
+
toolchain: options.toolchain,
|
|
41
48
|
js: options.typescript ? 'ts' : 'js',
|
|
42
49
|
jsx: options.typescript ? 'tsx' : 'jsx',
|
|
43
50
|
fileRouter: options.mode === FILE_ROUTER,
|
|
@@ -97,6 +104,20 @@ async function createPackageJSON(projectName, options, templateDir, routerDir, t
|
|
|
97
104
|
},
|
|
98
105
|
};
|
|
99
106
|
}
|
|
107
|
+
if (options.toolchain === 'biome') {
|
|
108
|
+
const biomePackageJSON = JSON.parse(await readFile(resolve(templateDir, 'package.biome.json'), 'utf8'));
|
|
109
|
+
packageJSON = {
|
|
110
|
+
...packageJSON,
|
|
111
|
+
scripts: {
|
|
112
|
+
...packageJSON.scripts,
|
|
113
|
+
...biomePackageJSON.scripts,
|
|
114
|
+
},
|
|
115
|
+
devDependencies: {
|
|
116
|
+
...packageJSON.devDependencies,
|
|
117
|
+
...biomePackageJSON.devDependencies,
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
100
121
|
if (options.mode === FILE_ROUTER) {
|
|
101
122
|
const frPackageJSON = JSON.parse(await readFile(resolve(routerDir, 'package.fr.json'), 'utf8'));
|
|
102
123
|
packageJSON = {
|
|
@@ -184,7 +205,14 @@ export async function createApp(options, { silent = false, } = {}) {
|
|
|
184
205
|
await mkdir(targetDir, { recursive: true });
|
|
185
206
|
// Setup the .vscode directory
|
|
186
207
|
await mkdir(resolve(targetDir, '.vscode'), { recursive: true });
|
|
187
|
-
|
|
208
|
+
switch (options.toolchain) {
|
|
209
|
+
case 'biome':
|
|
210
|
+
await copyFile(resolve(templateDirBase, '_dot_vscode/settings.biome.json'), resolve(targetDir, '.vscode/settings.json'));
|
|
211
|
+
break;
|
|
212
|
+
case 'none':
|
|
213
|
+
default:
|
|
214
|
+
await copyFile(resolve(templateDirBase, '_dot_vscode/settings.json'), resolve(targetDir, '.vscode/settings.json'));
|
|
215
|
+
}
|
|
188
216
|
// Fill the public directory
|
|
189
217
|
await mkdir(resolve(targetDir, 'public'), { recursive: true });
|
|
190
218
|
copyFiles(templateDirBase, [
|
|
@@ -211,6 +239,9 @@ export async function createApp(options, { silent = false, } = {}) {
|
|
|
211
239
|
await templateFile(templateDirBase, './vite.config.js.ejs');
|
|
212
240
|
await templateFile(templateDirBase, './src/styles.css.ejs');
|
|
213
241
|
copyFiles(templateDirBase, ['./src/logo.svg']);
|
|
242
|
+
if (options.toolchain === 'biome') {
|
|
243
|
+
copyFiles(templateDirBase, ['./toolchain/biome.json'], true);
|
|
244
|
+
}
|
|
214
245
|
// Setup the main, reportWebVitals and index.html files
|
|
215
246
|
if (!isAddOnEnabled('start') && options.framework === 'react') {
|
|
216
247
|
if (options.typescript) {
|
|
@@ -227,7 +258,7 @@ export async function createApp(options, { silent = false, } = {}) {
|
|
|
227
258
|
if (options.typescript) {
|
|
228
259
|
await templateFile(templateDirBase, './tsconfig.json.ejs', './tsconfig.json');
|
|
229
260
|
}
|
|
230
|
-
// Setup the package.json file, optionally with typescript and
|
|
261
|
+
// Setup the package.json file, optionally with typescript, tailwind and biome
|
|
231
262
|
await createPackageJSON(options.projectName, options, templateDirBase, templateDirRouter, targetDir, options.chosenAddOns.map((addOn) => addOn.packageAdditions));
|
|
232
263
|
// Copy all the asset files from the addons
|
|
233
264
|
const s = silent ? null : spinner();
|
|
@@ -355,6 +386,23 @@ export async function createApp(options, { silent = false, } = {}) {
|
|
|
355
386
|
log.warn(chalk.red(warnings.join('\n')));
|
|
356
387
|
}
|
|
357
388
|
}
|
|
389
|
+
if (options.toolchain === 'biome') {
|
|
390
|
+
s?.start(`Applying toolchain ${options.toolchain}...`);
|
|
391
|
+
switch (options.packageManager) {
|
|
392
|
+
case 'pnpm':
|
|
393
|
+
// pnpm automatically forwards extra arguments
|
|
394
|
+
await execa(options.packageManager, ['run', 'check', '--fix'], {
|
|
395
|
+
cwd: targetDir,
|
|
396
|
+
});
|
|
397
|
+
break;
|
|
398
|
+
default:
|
|
399
|
+
await execa(options.packageManager, ['run', 'check', '--', '--fix'], {
|
|
400
|
+
cwd: targetDir,
|
|
401
|
+
});
|
|
402
|
+
break;
|
|
403
|
+
}
|
|
404
|
+
s?.stop(`Applied toolchain ${options.toolchain}...`);
|
|
405
|
+
}
|
|
358
406
|
if (options.git) {
|
|
359
407
|
s?.start(`Initializing git repository...`);
|
|
360
408
|
await execa('git', ['init'], { cwd: targetDir });
|
package/dist/mcp.js
CHANGED
|
@@ -46,6 +46,10 @@ const tanStackReactAddOns = [
|
|
|
46
46
|
id: 'store',
|
|
47
47
|
description: 'Enable the TanStack Store state management library',
|
|
48
48
|
},
|
|
49
|
+
{
|
|
50
|
+
id: 'tanchat',
|
|
51
|
+
description: 'Add an AI chatbot example to the application',
|
|
52
|
+
},
|
|
49
53
|
];
|
|
50
54
|
server.tool('listTanStackReactAddOns', {}, () => {
|
|
51
55
|
return {
|
|
@@ -80,6 +84,7 @@ server.tool('createTanStackReactApplication', {
|
|
|
80
84
|
typescript: true,
|
|
81
85
|
tailwind: true,
|
|
82
86
|
packageManager: 'pnpm',
|
|
87
|
+
toolchain: 'none',
|
|
83
88
|
mode: 'file-router',
|
|
84
89
|
addOns: true,
|
|
85
90
|
chosenAddOns,
|
|
@@ -121,6 +126,10 @@ const tanStackSolidAddOns = [
|
|
|
121
126
|
id: 'tanstack-query',
|
|
122
127
|
description: 'Enable TanStack Query for data fetching',
|
|
123
128
|
},
|
|
129
|
+
{
|
|
130
|
+
id: 'tanchat',
|
|
131
|
+
description: 'Add an AI chatbot example to the application',
|
|
132
|
+
},
|
|
124
133
|
];
|
|
125
134
|
server.tool('listTanStackSolidAddOns', {}, () => {
|
|
126
135
|
return {
|
|
@@ -145,6 +154,7 @@ server.tool('createTanStackSolidApplication', {
|
|
|
145
154
|
typescript: true,
|
|
146
155
|
tailwind: true,
|
|
147
156
|
packageManager: 'pnpm',
|
|
157
|
+
toolchain: 'none',
|
|
148
158
|
mode: 'file-router',
|
|
149
159
|
addOns: true,
|
|
150
160
|
chosenAddOns,
|
package/dist/options.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { cancel, confirm, isCancel, multiselect, select, text, } from '@clack/prompts';
|
|
2
2
|
import { DEFAULT_PACKAGE_MANAGER, SUPPORTED_PACKAGE_MANAGERS, getPackageManager, } from './package-manager.js';
|
|
3
|
+
import { DEFAULT_TOOLCHAIN, SUPPORTED_TOOLCHAINS } from './toolchain.js';
|
|
3
4
|
import { CODE_ROUTER, DEFAULT_FRAMEWORK, FILE_ROUTER } from './constants.js';
|
|
4
5
|
import { finalizeAddOns, getAllAddOns } from './add-ons.js';
|
|
5
6
|
// If all CLI options are provided, use them directly
|
|
@@ -26,6 +27,7 @@ export async function normalizeOptions(cliOptions) {
|
|
|
26
27
|
typescript,
|
|
27
28
|
tailwind,
|
|
28
29
|
packageManager: cliOptions.packageManager || DEFAULT_PACKAGE_MANAGER,
|
|
30
|
+
toolchain: cliOptions.toolchain || DEFAULT_TOOLCHAIN,
|
|
29
31
|
mode: cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER,
|
|
30
32
|
git: !!cliOptions.git,
|
|
31
33
|
addOns,
|
|
@@ -145,7 +147,7 @@ export async function promptForOptions(cliOptions) {
|
|
|
145
147
|
}
|
|
146
148
|
}
|
|
147
149
|
// Tailwind selection
|
|
148
|
-
if (cliOptions.tailwind
|
|
150
|
+
if (!cliOptions.tailwind && options.framework === 'react') {
|
|
149
151
|
const tailwind = await confirm({
|
|
150
152
|
message: 'Would you like to use Tailwind CSS?',
|
|
151
153
|
initialValue: true,
|
|
@@ -184,6 +186,25 @@ export async function promptForOptions(cliOptions) {
|
|
|
184
186
|
else {
|
|
185
187
|
options.packageManager = cliOptions.packageManager;
|
|
186
188
|
}
|
|
189
|
+
// Toolchain selection
|
|
190
|
+
if (cliOptions.toolchain === undefined) {
|
|
191
|
+
const tc = await select({
|
|
192
|
+
message: 'Select toolchain',
|
|
193
|
+
options: SUPPORTED_TOOLCHAINS.map((tc) => ({
|
|
194
|
+
value: tc,
|
|
195
|
+
label: tc,
|
|
196
|
+
})),
|
|
197
|
+
initialValue: DEFAULT_TOOLCHAIN,
|
|
198
|
+
});
|
|
199
|
+
if (isCancel(tc)) {
|
|
200
|
+
cancel('Operation cancelled.');
|
|
201
|
+
process.exit(0);
|
|
202
|
+
}
|
|
203
|
+
options.toolchain = tc;
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
options.toolchain = cliOptions.toolchain;
|
|
207
|
+
}
|
|
187
208
|
options.chosenAddOns = [];
|
|
188
209
|
if (Array.isArray(cliOptions.addOns)) {
|
|
189
210
|
options.chosenAddOns = await finalizeAddOns(options.framework, options.mode, cliOptions.addOns);
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -4,12 +4,14 @@ import { intro, log } from '@clack/prompts'
|
|
|
4
4
|
import { createApp } from './create-app.js'
|
|
5
5
|
import { normalizeOptions, promptForOptions } from './options.js'
|
|
6
6
|
import { SUPPORTED_PACKAGE_MANAGERS } from './package-manager.js'
|
|
7
|
+
import { SUPPORTED_TOOLCHAINS } from './toolchain.js'
|
|
7
8
|
|
|
8
9
|
import runServer from './mcp.js'
|
|
9
10
|
import { listAddOns } from './add-ons.js'
|
|
10
11
|
import { DEFAULT_FRAMEWORK, SUPPORTED_FRAMEWORKS } from './constants.js'
|
|
11
12
|
|
|
12
13
|
import type { PackageManager } from './package-manager.js'
|
|
14
|
+
import type { ToolChain } from './toolchain.js'
|
|
13
15
|
import type { CliOptions, Framework } from './types.js'
|
|
14
16
|
|
|
15
17
|
export function cli() {
|
|
@@ -65,6 +67,20 @@ export function cli() {
|
|
|
65
67
|
return value as PackageManager
|
|
66
68
|
},
|
|
67
69
|
)
|
|
70
|
+
.option<ToolChain>(
|
|
71
|
+
`--toolchain <${SUPPORTED_TOOLCHAINS.join('|')}>`,
|
|
72
|
+
`Explicitly tell the CLI to use this toolchain`,
|
|
73
|
+
(value) => {
|
|
74
|
+
if (!SUPPORTED_TOOLCHAINS.includes(value as ToolChain)) {
|
|
75
|
+
throw new InvalidArgumentError(
|
|
76
|
+
`Invalid toolchain: ${value}. The following are allowed: ${SUPPORTED_TOOLCHAINS.join(
|
|
77
|
+
', ',
|
|
78
|
+
)}`,
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
return value as ToolChain
|
|
82
|
+
},
|
|
83
|
+
)
|
|
68
84
|
.option('--tailwind', 'add Tailwind CSS', false)
|
|
69
85
|
.option<Array<string> | boolean>(
|
|
70
86
|
'--add-ons [...add-ons]',
|
package/src/create-app.ts
CHANGED
|
@@ -30,9 +30,18 @@ function sortObject(obj: Record<string, string>): Record<string, string> {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
function createCopyFiles(targetDir: string) {
|
|
33
|
-
return async function copyFiles(
|
|
33
|
+
return async function copyFiles(
|
|
34
|
+
templateDir: string,
|
|
35
|
+
files: Array<string>,
|
|
36
|
+
// optionally copy files from a folder to the root
|
|
37
|
+
toRoot?: boolean,
|
|
38
|
+
) {
|
|
34
39
|
for (const file of files) {
|
|
35
|
-
|
|
40
|
+
let targetFileName = file.replace('.tw', '')
|
|
41
|
+
if (toRoot) {
|
|
42
|
+
const fileNoPath = targetFileName.split('/').pop()
|
|
43
|
+
targetFileName = fileNoPath ? `./${fileNoPath}` : targetFileName
|
|
44
|
+
}
|
|
36
45
|
await copyFile(
|
|
37
46
|
resolve(templateDir, file),
|
|
38
47
|
resolve(targetDir, targetFileName),
|
|
@@ -64,6 +73,7 @@ function createTemplateFile(
|
|
|
64
73
|
projectName: projectName,
|
|
65
74
|
typescript: options.typescript,
|
|
66
75
|
tailwind: options.tailwind,
|
|
76
|
+
toolchain: options.toolchain,
|
|
67
77
|
js: options.typescript ? 'ts' : 'js',
|
|
68
78
|
jsx: options.typescript ? 'tsx' : 'jsx',
|
|
69
79
|
fileRouter: options.mode === FILE_ROUTER,
|
|
@@ -147,6 +157,22 @@ async function createPackageJSON(
|
|
|
147
157
|
},
|
|
148
158
|
}
|
|
149
159
|
}
|
|
160
|
+
if (options.toolchain === 'biome') {
|
|
161
|
+
const biomePackageJSON = JSON.parse(
|
|
162
|
+
await readFile(resolve(templateDir, 'package.biome.json'), 'utf8'),
|
|
163
|
+
)
|
|
164
|
+
packageJSON = {
|
|
165
|
+
...packageJSON,
|
|
166
|
+
scripts: {
|
|
167
|
+
...packageJSON.scripts,
|
|
168
|
+
...biomePackageJSON.scripts,
|
|
169
|
+
},
|
|
170
|
+
devDependencies: {
|
|
171
|
+
...packageJSON.devDependencies,
|
|
172
|
+
...biomePackageJSON.devDependencies,
|
|
173
|
+
},
|
|
174
|
+
}
|
|
175
|
+
}
|
|
150
176
|
if (options.mode === FILE_ROUTER) {
|
|
151
177
|
const frPackageJSON = JSON.parse(
|
|
152
178
|
await readFile(resolve(routerDir, 'package.fr.json'), 'utf8'),
|
|
@@ -282,10 +308,20 @@ export async function createApp(
|
|
|
282
308
|
|
|
283
309
|
// Setup the .vscode directory
|
|
284
310
|
await mkdir(resolve(targetDir, '.vscode'), { recursive: true })
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
311
|
+
switch (options.toolchain) {
|
|
312
|
+
case 'biome':
|
|
313
|
+
await copyFile(
|
|
314
|
+
resolve(templateDirBase, '_dot_vscode/settings.biome.json'),
|
|
315
|
+
resolve(targetDir, '.vscode/settings.json'),
|
|
316
|
+
)
|
|
317
|
+
break
|
|
318
|
+
case 'none':
|
|
319
|
+
default:
|
|
320
|
+
await copyFile(
|
|
321
|
+
resolve(templateDirBase, '_dot_vscode/settings.json'),
|
|
322
|
+
resolve(targetDir, '.vscode/settings.json'),
|
|
323
|
+
)
|
|
324
|
+
}
|
|
289
325
|
|
|
290
326
|
// Fill the public directory
|
|
291
327
|
await mkdir(resolve(targetDir, 'public'), { recursive: true })
|
|
@@ -321,6 +357,10 @@ export async function createApp(
|
|
|
321
357
|
|
|
322
358
|
copyFiles(templateDirBase, ['./src/logo.svg'])
|
|
323
359
|
|
|
360
|
+
if (options.toolchain === 'biome') {
|
|
361
|
+
copyFiles(templateDirBase, ['./toolchain/biome.json'], true)
|
|
362
|
+
}
|
|
363
|
+
|
|
324
364
|
// Setup the main, reportWebVitals and index.html files
|
|
325
365
|
if (!isAddOnEnabled('start') && options.framework === 'react') {
|
|
326
366
|
if (options.typescript) {
|
|
@@ -346,7 +386,7 @@ export async function createApp(
|
|
|
346
386
|
)
|
|
347
387
|
}
|
|
348
388
|
|
|
349
|
-
// Setup the package.json file, optionally with typescript and
|
|
389
|
+
// Setup the package.json file, optionally with typescript, tailwind and biome
|
|
350
390
|
await createPackageJSON(
|
|
351
391
|
options.projectName,
|
|
352
392
|
options,
|
|
@@ -567,6 +607,24 @@ export async function createApp(
|
|
|
567
607
|
}
|
|
568
608
|
}
|
|
569
609
|
|
|
610
|
+
if (options.toolchain === 'biome') {
|
|
611
|
+
s?.start(`Applying toolchain ${options.toolchain}...`)
|
|
612
|
+
switch (options.packageManager) {
|
|
613
|
+
case 'pnpm':
|
|
614
|
+
// pnpm automatically forwards extra arguments
|
|
615
|
+
await execa(options.packageManager, ['run', 'check', '--fix'], {
|
|
616
|
+
cwd: targetDir,
|
|
617
|
+
})
|
|
618
|
+
break
|
|
619
|
+
default:
|
|
620
|
+
await execa(options.packageManager, ['run', 'check', '--', '--fix'], {
|
|
621
|
+
cwd: targetDir,
|
|
622
|
+
})
|
|
623
|
+
break
|
|
624
|
+
}
|
|
625
|
+
s?.stop(`Applied toolchain ${options.toolchain}...`)
|
|
626
|
+
}
|
|
627
|
+
|
|
570
628
|
if (options.git) {
|
|
571
629
|
s?.start(`Initializing git repository...`)
|
|
572
630
|
await execa('git', ['init'], { cwd: targetDir })
|
package/src/mcp.ts
CHANGED
|
@@ -50,6 +50,10 @@ const tanStackReactAddOns = [
|
|
|
50
50
|
id: 'store',
|
|
51
51
|
description: 'Enable the TanStack Store state management library',
|
|
52
52
|
},
|
|
53
|
+
{
|
|
54
|
+
id: 'tanchat',
|
|
55
|
+
description: 'Add an AI chatbot example to the application',
|
|
56
|
+
},
|
|
53
57
|
]
|
|
54
58
|
|
|
55
59
|
server.tool('listTanStackReactAddOns', {}, () => {
|
|
@@ -98,6 +102,7 @@ server.tool(
|
|
|
98
102
|
typescript: true,
|
|
99
103
|
tailwind: true,
|
|
100
104
|
packageManager: 'pnpm',
|
|
105
|
+
toolchain: 'none',
|
|
101
106
|
mode: 'file-router',
|
|
102
107
|
addOns: true,
|
|
103
108
|
chosenAddOns,
|
|
@@ -142,6 +147,10 @@ const tanStackSolidAddOns = [
|
|
|
142
147
|
id: 'tanstack-query',
|
|
143
148
|
description: 'Enable TanStack Query for data fetching',
|
|
144
149
|
},
|
|
150
|
+
{
|
|
151
|
+
id: 'tanchat',
|
|
152
|
+
description: 'Add an AI chatbot example to the application',
|
|
153
|
+
},
|
|
145
154
|
]
|
|
146
155
|
|
|
147
156
|
server.tool('listTanStackSolidAddOns', {}, () => {
|
|
@@ -178,6 +187,7 @@ server.tool(
|
|
|
178
187
|
typescript: true,
|
|
179
188
|
tailwind: true,
|
|
180
189
|
packageManager: 'pnpm',
|
|
190
|
+
toolchain: 'none',
|
|
181
191
|
mode: 'file-router',
|
|
182
192
|
addOns: true,
|
|
183
193
|
chosenAddOns,
|
package/src/options.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
SUPPORTED_PACKAGE_MANAGERS,
|
|
13
13
|
getPackageManager,
|
|
14
14
|
} from './package-manager.js'
|
|
15
|
+
import { DEFAULT_TOOLCHAIN, SUPPORTED_TOOLCHAINS } from './toolchain.js'
|
|
15
16
|
import { CODE_ROUTER, DEFAULT_FRAMEWORK, FILE_ROUTER } from './constants.js'
|
|
16
17
|
import { finalizeAddOns, getAllAddOns } from './add-ons.js'
|
|
17
18
|
import type { AddOn, Variable } from './add-ons.js'
|
|
@@ -52,6 +53,7 @@ export async function normalizeOptions(
|
|
|
52
53
|
typescript,
|
|
53
54
|
tailwind,
|
|
54
55
|
packageManager: cliOptions.packageManager || DEFAULT_PACKAGE_MANAGER,
|
|
56
|
+
toolchain: cliOptions.toolchain || DEFAULT_TOOLCHAIN,
|
|
55
57
|
mode: cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER,
|
|
56
58
|
git: !!cliOptions.git,
|
|
57
59
|
addOns,
|
|
@@ -181,7 +183,7 @@ export async function promptForOptions(
|
|
|
181
183
|
}
|
|
182
184
|
|
|
183
185
|
// Tailwind selection
|
|
184
|
-
if (cliOptions.tailwind
|
|
186
|
+
if (!cliOptions.tailwind && options.framework === 'react') {
|
|
185
187
|
const tailwind = await confirm({
|
|
186
188
|
message: 'Would you like to use Tailwind CSS?',
|
|
187
189
|
initialValue: true,
|
|
@@ -219,6 +221,25 @@ export async function promptForOptions(
|
|
|
219
221
|
options.packageManager = cliOptions.packageManager
|
|
220
222
|
}
|
|
221
223
|
|
|
224
|
+
// Toolchain selection
|
|
225
|
+
if (cliOptions.toolchain === undefined) {
|
|
226
|
+
const tc = await select({
|
|
227
|
+
message: 'Select toolchain',
|
|
228
|
+
options: SUPPORTED_TOOLCHAINS.map((tc) => ({
|
|
229
|
+
value: tc,
|
|
230
|
+
label: tc,
|
|
231
|
+
})),
|
|
232
|
+
initialValue: DEFAULT_TOOLCHAIN,
|
|
233
|
+
})
|
|
234
|
+
if (isCancel(tc)) {
|
|
235
|
+
cancel('Operation cancelled.')
|
|
236
|
+
process.exit(0)
|
|
237
|
+
}
|
|
238
|
+
options.toolchain = tc
|
|
239
|
+
} else {
|
|
240
|
+
options.toolchain = cliOptions.toolchain
|
|
241
|
+
}
|
|
242
|
+
|
|
222
243
|
options.chosenAddOns = []
|
|
223
244
|
if (Array.isArray(cliOptions.addOns)) {
|
|
224
245
|
options.chosenAddOns = await finalizeAddOns(
|
package/src/toolchain.ts
ADDED
package/src/types.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AddOn } from './add-ons.js'
|
|
2
2
|
import type { CODE_ROUTER, FILE_ROUTER } from './constants.js'
|
|
3
3
|
import type { PackageManager } from './package-manager.js'
|
|
4
|
+
import type { ToolChain } from './toolchain.js'
|
|
4
5
|
|
|
5
6
|
export type Framework = 'solid' | 'react'
|
|
6
7
|
|
|
@@ -10,6 +11,7 @@ export interface Options {
|
|
|
10
11
|
typescript: boolean
|
|
11
12
|
tailwind: boolean
|
|
12
13
|
packageManager: PackageManager
|
|
14
|
+
toolchain: ToolChain
|
|
13
15
|
mode: typeof CODE_ROUTER | typeof FILE_ROUTER
|
|
14
16
|
addOns: boolean
|
|
15
17
|
chosenAddOns: Array<AddOn>
|
|
@@ -22,6 +24,7 @@ export interface CliOptions {
|
|
|
22
24
|
framework?: Framework
|
|
23
25
|
tailwind?: boolean
|
|
24
26
|
packageManager?: PackageManager
|
|
27
|
+
toolchain?: ToolChain
|
|
25
28
|
projectName?: string
|
|
26
29
|
git?: boolean
|
|
27
30
|
addOns?: Array<string> | boolean
|
|
@@ -31,6 +31,16 @@ This project uses [Tailwind CSS](https://tailwindcss.com/) for styling.
|
|
|
31
31
|
<% } else { %>
|
|
32
32
|
This project uses CSS for styling.
|
|
33
33
|
<% } %>
|
|
34
|
+
<% if (toolchain && toolchain === 'biome') { %>
|
|
35
|
+
## Linting & Formatting
|
|
36
|
+
This project uses [Biome](https://biomejs.dev/) for linting and formatting. The following scripts are available:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
<%= packageManager %> run lint
|
|
40
|
+
<%= packageManager %> run format
|
|
41
|
+
<%= packageManager %> run check # runs both lint and format together
|
|
42
|
+
```
|
|
43
|
+
<% } %>
|
|
34
44
|
|
|
35
45
|
<% for(const addon of addOns.filter(addon => addon.readme)) { %>
|
|
36
46
|
<%- addon.readme %>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"files.watcherExclude": {
|
|
3
|
+
"**/routeTree.gen.ts": true
|
|
4
|
+
},
|
|
5
|
+
"search.exclude": {
|
|
6
|
+
"**/routeTree.gen.ts": true
|
|
7
|
+
},
|
|
8
|
+
"files.readonlyInclude": {
|
|
9
|
+
"**/routeTree.gen.ts": true
|
|
10
|
+
},
|
|
11
|
+
"[javascript]": {
|
|
12
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
13
|
+
},
|
|
14
|
+
"[javascriptreact]": {
|
|
15
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
16
|
+
},
|
|
17
|
+
"[typescript]": {
|
|
18
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
19
|
+
},
|
|
20
|
+
"[typescriptreact]": {
|
|
21
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
22
|
+
},
|
|
23
|
+
"[json]": {
|
|
24
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
25
|
+
},
|
|
26
|
+
"[jsonc]": {
|
|
27
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
28
|
+
},
|
|
29
|
+
"[html]": {
|
|
30
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
31
|
+
},
|
|
32
|
+
"[css]": {
|
|
33
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
34
|
+
},
|
|
35
|
+
"editor.codeActionsOnSave": {
|
|
36
|
+
"source.organizeImports.biome": "explicit"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
|
3
|
+
"vcs": {
|
|
4
|
+
"enabled": false,
|
|
5
|
+
"clientKind": "git",
|
|
6
|
+
"useIgnoreFile": false
|
|
7
|
+
},
|
|
8
|
+
"files": {
|
|
9
|
+
"ignoreUnknown": false,
|
|
10
|
+
"ignore": ["src/routeTree.gen.ts"],
|
|
11
|
+
"include": ["src/*", ".vscode/*", "index.html", "vite.config.js"]
|
|
12
|
+
},
|
|
13
|
+
"formatter": {
|
|
14
|
+
"enabled": true,
|
|
15
|
+
"indentStyle": "tab"
|
|
16
|
+
},
|
|
17
|
+
"organizeImports": {
|
|
18
|
+
"enabled": true
|
|
19
|
+
},
|
|
20
|
+
"linter": {
|
|
21
|
+
"enabled": true,
|
|
22
|
+
"rules": {
|
|
23
|
+
"recommended": true
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"javascript": {
|
|
27
|
+
"formatter": {
|
|
28
|
+
"quoteStyle": "double"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -60,10 +60,10 @@ declare module "@tanstack/react-router" {
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
const rootElement = document.getElementById("app")
|
|
63
|
+
const rootElement = document.getElementById("app");
|
|
64
64
|
<% } else { %>
|
|
65
65
|
const rootElement = document.getElementById("app");
|
|
66
|
-
<% } %>if (!rootElement.innerHTML) {
|
|
66
|
+
<% } %>if (rootElement && !rootElement.innerHTML) {
|
|
67
67
|
const root = ReactDOM.createRoot(rootElement);
|
|
68
68
|
root.render(
|
|
69
69
|
<StrictMode>
|
|
@@ -19,8 +19,8 @@ declare module "@tanstack/react-router" {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
// Render the app
|
|
22
|
-
const rootElement = document.getElementById("app")
|
|
23
|
-
if (!rootElement.innerHTML) {
|
|
22
|
+
const rootElement = document.getElementById("app");
|
|
23
|
+
if (rootElement && !rootElement.innerHTML) {
|
|
24
24
|
const root = ReactDOM.createRoot(rootElement);
|
|
25
25
|
root.render(
|
|
26
26
|
<StrictMode>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"files.watcherExclude": {
|
|
3
|
+
"**/routeTree.gen.ts": true
|
|
4
|
+
},
|
|
5
|
+
"search.exclude": {
|
|
6
|
+
"**/routeTree.gen.ts": true
|
|
7
|
+
},
|
|
8
|
+
"files.readonlyInclude": {
|
|
9
|
+
"**/routeTree.gen.ts": true
|
|
10
|
+
},
|
|
11
|
+
"[javascript]": {
|
|
12
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
13
|
+
},
|
|
14
|
+
"[javascriptreact]": {
|
|
15
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
16
|
+
},
|
|
17
|
+
"[typescript]": {
|
|
18
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
19
|
+
},
|
|
20
|
+
"[typescriptreact]": {
|
|
21
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
22
|
+
},
|
|
23
|
+
"[json]": {
|
|
24
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
25
|
+
},
|
|
26
|
+
"[jsonc]": {
|
|
27
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
28
|
+
},
|
|
29
|
+
"[html]": {
|
|
30
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
31
|
+
},
|
|
32
|
+
"[css]": {
|
|
33
|
+
"editor.defaultFormatter": "biomejs.biome"
|
|
34
|
+
},
|
|
35
|
+
"editor.codeActionsOnSave": {
|
|
36
|
+
"source.organizeImports.biome": "explicit"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
|
3
|
+
"vcs": {
|
|
4
|
+
"enabled": false,
|
|
5
|
+
"clientKind": "git",
|
|
6
|
+
"useIgnoreFile": false
|
|
7
|
+
},
|
|
8
|
+
"files": {
|
|
9
|
+
"ignoreUnknown": false,
|
|
10
|
+
"ignore": ["src/routeTree.gen.ts"],
|
|
11
|
+
"include": ["src/*", ".vscode/*", "index.html", "vite.config.js"]
|
|
12
|
+
},
|
|
13
|
+
"formatter": {
|
|
14
|
+
"enabled": true,
|
|
15
|
+
"indentStyle": "tab"
|
|
16
|
+
},
|
|
17
|
+
"organizeImports": {
|
|
18
|
+
"enabled": true
|
|
19
|
+
},
|
|
20
|
+
"linter": {
|
|
21
|
+
"enabled": true,
|
|
22
|
+
"rules": {
|
|
23
|
+
"recommended": true
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"javascript": {
|
|
27
|
+
"formatter": {
|
|
28
|
+
"quoteStyle": "double"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|