create-start-app 0.6.1 → 0.6.3
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/cli.js +4 -1
- package/dist/create-app.js +54 -74
- package/dist/environment.js +43 -0
- package/dist/mcp.js +3 -0
- package/package.json +4 -2
- package/src/cli.ts +5 -1
- package/src/create-app.ts +92 -81
- package/src/environment.ts +70 -0
- package/src/mcp.ts +3 -0
- package/templates/react/add-on/form/assets/src/components/demo.FormComponents.tsx +120 -0
- package/templates/react/add-on/form/assets/src/hooks/demo.form-context.ts +4 -0
- package/templates/react/add-on/form/assets/src/hooks/demo.form.ts +22 -0
- package/templates/react/add-on/form/assets/src/routes/demo.form.address.tsx.ejs +203 -0
- package/templates/react/add-on/form/assets/src/routes/demo.form.simple.tsx.ejs +79 -0
- package/templates/react/add-on/form/info.json +6 -2
- package/templates/react/add-on/form/package.json +2 -1
- package/templates/react/base/README.md.ejs +1 -1
- package/templates/react/example/tanchat/info.json +1 -1
- package/templates/solid/add-on/form/assets/src/routes/demo.form.tsx.ejs +310 -106
- package/templates/solid/add-on/form/package.json +1 -1
- package/tests/cra.test.ts +112 -0
- package/tests/snapshots/cra/cr-js-npm.json +34 -0
- package/tests/snapshots/cra/cr-ts-npm.json +35 -0
- package/tests/snapshots/cra/fr-ts-npm.json +35 -0
- package/tests/snapshots/cra/fr-ts-tw-npm.json +34 -0
- package/tests/test-utilities.ts +69 -0
- package/templates/react/add-on/form/assets/src/routes/demo.form.tsx.ejs +0 -62
package/dist/cli.js
CHANGED
|
@@ -7,6 +7,7 @@ import { SUPPORTED_TOOLCHAINS } from './toolchain.js';
|
|
|
7
7
|
import runServer from './mcp.js';
|
|
8
8
|
import { listAddOns } from './add-ons.js';
|
|
9
9
|
import { DEFAULT_FRAMEWORK, SUPPORTED_FRAMEWORKS } from './constants.js';
|
|
10
|
+
import { createDefaultEnvironment } from './environment.js';
|
|
10
11
|
export function cli() {
|
|
11
12
|
const program = new Command();
|
|
12
13
|
program
|
|
@@ -72,7 +73,9 @@ export function cli() {
|
|
|
72
73
|
intro("Let's configure your TanStack application");
|
|
73
74
|
finalOptions = await promptForOptions(cliOptions);
|
|
74
75
|
}
|
|
75
|
-
await createApp(finalOptions
|
|
76
|
+
await createApp(finalOptions, {
|
|
77
|
+
environment: createDefaultEnvironment(),
|
|
78
|
+
});
|
|
76
79
|
}
|
|
77
80
|
catch (error) {
|
|
78
81
|
log.error(error instanceof Error
|
package/dist/create-app.js
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { appendFile, copyFile, mkdir, readFile, writeFile, } from 'node:fs/promises';
|
|
3
|
-
import { existsSync, readdirSync, statSync } from 'node:fs';
|
|
4
1
|
import { basename, dirname, resolve } from 'node:path';
|
|
5
2
|
import { fileURLToPath } from 'node:url';
|
|
6
3
|
import { log, outro, spinner } from '@clack/prompts';
|
|
7
|
-
import { execa } from 'execa';
|
|
8
4
|
import { render } from 'ejs';
|
|
9
5
|
import { format } from 'prettier';
|
|
10
6
|
import chalk from 'chalk';
|
|
@@ -17,7 +13,7 @@ function sortObject(obj) {
|
|
|
17
13
|
return acc;
|
|
18
14
|
}, {});
|
|
19
15
|
}
|
|
20
|
-
function createCopyFiles(targetDir) {
|
|
16
|
+
function createCopyFiles(environment, targetDir) {
|
|
21
17
|
return async function copyFiles(templateDir, files,
|
|
22
18
|
// optionally copy files from a folder to the root
|
|
23
19
|
toRoot) {
|
|
@@ -27,7 +23,7 @@ function createCopyFiles(targetDir) {
|
|
|
27
23
|
const fileNoPath = targetFileName.split('/').pop();
|
|
28
24
|
targetFileName = fileNoPath ? `./${fileNoPath}` : targetFileName;
|
|
29
25
|
}
|
|
30
|
-
await copyFile(resolve(templateDir, file), resolve(targetDir, targetFileName));
|
|
26
|
+
await environment.copyFile(resolve(templateDir, file), resolve(targetDir, targetFileName));
|
|
31
27
|
}
|
|
32
28
|
};
|
|
33
29
|
}
|
|
@@ -37,7 +33,7 @@ function jsSafeName(name) {
|
|
|
37
33
|
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
38
34
|
.join('');
|
|
39
35
|
}
|
|
40
|
-
function createTemplateFile(projectName, options, targetDir) {
|
|
36
|
+
function createTemplateFile(environment, projectName, options, targetDir) {
|
|
41
37
|
return async function templateFile(templateDir, file, targetFileName, extraTemplateValues) {
|
|
42
38
|
const templateValues = {
|
|
43
39
|
packageManager: options.packageManager,
|
|
@@ -56,7 +52,7 @@ function createTemplateFile(projectName, options, targetDir) {
|
|
|
56
52
|
addOns: options.chosenAddOns,
|
|
57
53
|
...extraTemplateValues,
|
|
58
54
|
};
|
|
59
|
-
const template = await readFile(resolve(templateDir, file), 'utf-8');
|
|
55
|
+
const template = await environment.readFile(resolve(templateDir, file), 'utf-8');
|
|
60
56
|
let content = '';
|
|
61
57
|
try {
|
|
62
58
|
content = render(template, templateValues);
|
|
@@ -75,17 +71,14 @@ function createTemplateFile(projectName, options, targetDir) {
|
|
|
75
71
|
parser: 'typescript',
|
|
76
72
|
});
|
|
77
73
|
}
|
|
78
|
-
await
|
|
79
|
-
recursive: true,
|
|
80
|
-
});
|
|
81
|
-
await writeFile(resolve(targetDir, target), content);
|
|
74
|
+
await environment.writeFile(resolve(targetDir, target), content);
|
|
82
75
|
};
|
|
83
76
|
}
|
|
84
|
-
async function createPackageJSON(projectName, options, templateDir, routerDir, targetDir, addOns) {
|
|
85
|
-
let packageJSON = JSON.parse(await readFile(resolve(templateDir, 'package.json'), 'utf8'));
|
|
77
|
+
async function createPackageJSON(environment, projectName, options, templateDir, routerDir, targetDir, addOns) {
|
|
78
|
+
let packageJSON = JSON.parse(await environment.readFile(resolve(templateDir, 'package.json'), 'utf8'));
|
|
86
79
|
packageJSON.name = projectName;
|
|
87
80
|
if (options.typescript) {
|
|
88
|
-
const tsPackageJSON = JSON.parse(await readFile(resolve(templateDir, 'package.ts.json'), 'utf8'));
|
|
81
|
+
const tsPackageJSON = JSON.parse(await environment.readFile(resolve(templateDir, 'package.ts.json'), 'utf8'));
|
|
89
82
|
packageJSON = {
|
|
90
83
|
...packageJSON,
|
|
91
84
|
devDependencies: {
|
|
@@ -95,7 +88,7 @@ async function createPackageJSON(projectName, options, templateDir, routerDir, t
|
|
|
95
88
|
};
|
|
96
89
|
}
|
|
97
90
|
if (options.tailwind) {
|
|
98
|
-
const twPackageJSON = JSON.parse(await readFile(resolve(templateDir, 'package.tw.json'), 'utf8'));
|
|
91
|
+
const twPackageJSON = JSON.parse(await environment.readFile(resolve(templateDir, 'package.tw.json'), 'utf8'));
|
|
99
92
|
packageJSON = {
|
|
100
93
|
...packageJSON,
|
|
101
94
|
dependencies: {
|
|
@@ -105,7 +98,7 @@ async function createPackageJSON(projectName, options, templateDir, routerDir, t
|
|
|
105
98
|
};
|
|
106
99
|
}
|
|
107
100
|
if (options.toolchain === 'biome') {
|
|
108
|
-
const biomePackageJSON = JSON.parse(await readFile(resolve(templateDir, 'package.biome.json'), 'utf8'));
|
|
101
|
+
const biomePackageJSON = JSON.parse(await environment.readFile(resolve(templateDir, 'package.biome.json'), 'utf8'));
|
|
109
102
|
packageJSON = {
|
|
110
103
|
...packageJSON,
|
|
111
104
|
scripts: {
|
|
@@ -119,7 +112,7 @@ async function createPackageJSON(projectName, options, templateDir, routerDir, t
|
|
|
119
112
|
};
|
|
120
113
|
}
|
|
121
114
|
if (options.mode === FILE_ROUTER) {
|
|
122
|
-
const frPackageJSON = JSON.parse(await readFile(resolve(routerDir, 'package.fr.json'), 'utf8'));
|
|
115
|
+
const frPackageJSON = JSON.parse(await environment.readFile(resolve(routerDir, 'package.fr.json'), 'utf8'));
|
|
123
116
|
packageJSON = {
|
|
124
117
|
...packageJSON,
|
|
125
118
|
dependencies: {
|
|
@@ -147,16 +140,15 @@ async function createPackageJSON(projectName, options, templateDir, routerDir, t
|
|
|
147
140
|
}
|
|
148
141
|
packageJSON.dependencies = sortObject(packageJSON.dependencies);
|
|
149
142
|
packageJSON.devDependencies = sortObject(packageJSON.devDependencies);
|
|
150
|
-
await writeFile(resolve(targetDir, 'package.json'), JSON.stringify(packageJSON, null, 2));
|
|
143
|
+
await environment.writeFile(resolve(targetDir, 'package.json'), JSON.stringify(packageJSON, null, 2));
|
|
151
144
|
}
|
|
152
|
-
async function copyFilesRecursively(source, target,
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const files = readdirSync(source);
|
|
145
|
+
async function copyFilesRecursively(environment, source, target, templateFile) {
|
|
146
|
+
if (environment.isDirectory(source)) {
|
|
147
|
+
const files = environment.readdir(source);
|
|
156
148
|
for (const file of files) {
|
|
157
149
|
const sourceChild = resolve(source, file);
|
|
158
150
|
const targetChild = resolve(target, file);
|
|
159
|
-
await copyFilesRecursively(sourceChild, targetChild,
|
|
151
|
+
await copyFilesRecursively(environment, sourceChild, targetChild, templateFile);
|
|
160
152
|
}
|
|
161
153
|
}
|
|
162
154
|
else {
|
|
@@ -172,49 +164,43 @@ async function copyFilesRecursively(source, target, copyFile, templateFile) {
|
|
|
172
164
|
isAppend = true;
|
|
173
165
|
}
|
|
174
166
|
const targetPath = resolve(dirname(target), targetFile);
|
|
175
|
-
await mkdir(dirname(targetPath), {
|
|
176
|
-
recursive: true,
|
|
177
|
-
});
|
|
178
167
|
if (isTemplate) {
|
|
179
168
|
await templateFile(source, targetPath);
|
|
180
169
|
}
|
|
181
170
|
else {
|
|
182
171
|
if (isAppend) {
|
|
183
|
-
await appendFile(targetPath, (await readFile(source)).toString());
|
|
172
|
+
await environment.appendFile(targetPath, (await environment.readFile(source)).toString());
|
|
184
173
|
}
|
|
185
174
|
else {
|
|
186
|
-
await copyFile(source, targetPath);
|
|
175
|
+
await environment.copyFile(source, targetPath);
|
|
187
176
|
}
|
|
188
177
|
}
|
|
189
178
|
}
|
|
190
179
|
}
|
|
191
|
-
export async function createApp(options, { silent = false,
|
|
180
|
+
export async function createApp(options, { silent = false, environment, }) {
|
|
181
|
+
environment.startRun();
|
|
192
182
|
const templateDirBase = fileURLToPath(new URL(`../templates/${options.framework}/base`, import.meta.url));
|
|
193
183
|
const templateDirRouter = fileURLToPath(new URL(`../templates/${options.framework}/${options.mode}`, import.meta.url));
|
|
194
184
|
const targetDir = resolve(process.cwd(), options.projectName);
|
|
195
|
-
if (
|
|
185
|
+
if (environment.exists(targetDir)) {
|
|
196
186
|
if (!silent) {
|
|
197
187
|
log.error(`Directory "${options.projectName}" already exists`);
|
|
198
188
|
}
|
|
199
189
|
return;
|
|
200
190
|
}
|
|
201
|
-
const copyFiles = createCopyFiles(targetDir);
|
|
202
|
-
const templateFile = createTemplateFile(options.projectName, options, targetDir);
|
|
191
|
+
const copyFiles = createCopyFiles(environment, targetDir);
|
|
192
|
+
const templateFile = createTemplateFile(environment, options.projectName, options, targetDir);
|
|
203
193
|
const isAddOnEnabled = (id) => options.chosenAddOns.find((a) => a.id === id);
|
|
204
|
-
// Make the root directory
|
|
205
|
-
await mkdir(targetDir, { recursive: true });
|
|
206
194
|
// Setup the .vscode directory
|
|
207
|
-
await mkdir(resolve(targetDir, '.vscode'), { recursive: true });
|
|
208
195
|
switch (options.toolchain) {
|
|
209
196
|
case 'biome':
|
|
210
|
-
await copyFile(resolve(templateDirBase, '_dot_vscode/settings.biome.json'), resolve(targetDir, '.vscode/settings.json'));
|
|
197
|
+
await environment.copyFile(resolve(templateDirBase, '_dot_vscode/settings.biome.json'), resolve(targetDir, '.vscode/settings.json'));
|
|
211
198
|
break;
|
|
212
199
|
case 'none':
|
|
213
200
|
default:
|
|
214
|
-
await copyFile(resolve(templateDirBase, '_dot_vscode/settings.json'), resolve(targetDir, '.vscode/settings.json'));
|
|
201
|
+
await environment.copyFile(resolve(templateDirBase, '_dot_vscode/settings.json'), resolve(targetDir, '.vscode/settings.json'));
|
|
215
202
|
}
|
|
216
203
|
// Fill the public directory
|
|
217
|
-
await mkdir(resolve(targetDir, 'public'), { recursive: true });
|
|
218
204
|
copyFiles(templateDirBase, [
|
|
219
205
|
'./public/robots.txt',
|
|
220
206
|
'./public/favicon.ico',
|
|
@@ -222,15 +208,9 @@ export async function createApp(options, { silent = false, } = {}) {
|
|
|
222
208
|
'./public/logo192.png',
|
|
223
209
|
'./public/logo512.png',
|
|
224
210
|
]);
|
|
225
|
-
// Make the src directory
|
|
226
|
-
await mkdir(resolve(targetDir, 'src'), { recursive: true });
|
|
227
|
-
if (options.mode === FILE_ROUTER) {
|
|
228
|
-
await mkdir(resolve(targetDir, 'src/routes'), { recursive: true });
|
|
229
|
-
await mkdir(resolve(targetDir, 'src/components'), { recursive: true });
|
|
230
|
-
}
|
|
231
211
|
// Check for a .cursorrules file
|
|
232
|
-
if (
|
|
233
|
-
await copyFile(resolve(templateDirBase, '.cursorrules'), resolve(targetDir, '.cursorrules'));
|
|
212
|
+
if (environment.exists(resolve(templateDirBase, '.cursorrules'))) {
|
|
213
|
+
await environment.copyFile(resolve(templateDirBase, '.cursorrules'), resolve(targetDir, '.cursorrules'));
|
|
234
214
|
}
|
|
235
215
|
// Copy in Vite and Tailwind config and CSS
|
|
236
216
|
if (!options.tailwind) {
|
|
@@ -259,20 +239,18 @@ export async function createApp(options, { silent = false, } = {}) {
|
|
|
259
239
|
await templateFile(templateDirBase, './tsconfig.json.ejs', './tsconfig.json');
|
|
260
240
|
}
|
|
261
241
|
// Setup the package.json file, optionally with typescript, tailwind and biome
|
|
262
|
-
await createPackageJSON(options.projectName, options, templateDirBase, templateDirRouter, targetDir, options.chosenAddOns.map((addOn) => addOn.packageAdditions));
|
|
242
|
+
await createPackageJSON(environment, options.projectName, options, templateDirBase, templateDirRouter, targetDir, options.chosenAddOns.map((addOn) => addOn.packageAdditions));
|
|
263
243
|
// Copy all the asset files from the addons
|
|
264
244
|
const s = silent ? null : spinner();
|
|
265
245
|
for (const phase of ['setup', 'add-on', 'example']) {
|
|
266
246
|
for (const addOn of options.chosenAddOns.filter((addOn) => addOn.phase === phase)) {
|
|
267
247
|
s?.start(`Setting up ${addOn.name}...`);
|
|
268
248
|
const addOnDir = resolve(addOn.directory, 'assets');
|
|
269
|
-
if (
|
|
270
|
-
await copyFilesRecursively(addOnDir, targetDir,
|
|
249
|
+
if (environment.exists(addOnDir)) {
|
|
250
|
+
await copyFilesRecursively(environment, addOnDir, targetDir, async (file, targetFileName) => templateFile(addOnDir, file, targetFileName));
|
|
271
251
|
}
|
|
272
252
|
if (addOn.command) {
|
|
273
|
-
await
|
|
274
|
-
cwd: targetDir,
|
|
275
|
-
});
|
|
253
|
+
await environment.execute(addOn.command.command, addOn.command.args || [], targetDir);
|
|
276
254
|
}
|
|
277
255
|
s?.stop(`${addOn.name} setup complete`);
|
|
278
256
|
}
|
|
@@ -288,31 +266,29 @@ export async function createApp(options, { silent = false, } = {}) {
|
|
|
288
266
|
}
|
|
289
267
|
if (shadcnComponents.size > 0) {
|
|
290
268
|
s?.start(`Installing shadcn components (${Array.from(shadcnComponents).join(', ')})...`);
|
|
291
|
-
await
|
|
292
|
-
cwd: targetDir,
|
|
293
|
-
});
|
|
269
|
+
await environment.execute('npx', ['shadcn@canary', 'add', ...shadcnComponents], targetDir);
|
|
294
270
|
s?.stop(`Installed shadcn components`);
|
|
295
271
|
}
|
|
296
272
|
}
|
|
297
273
|
const integrations = [];
|
|
298
|
-
if (
|
|
299
|
-
for (const integration of
|
|
274
|
+
if (environment.exists(resolve(targetDir, 'src/integrations'))) {
|
|
275
|
+
for (const integration of environment.readdir(resolve(targetDir, 'src/integrations'))) {
|
|
300
276
|
const integrationName = jsSafeName(integration);
|
|
301
|
-
if (
|
|
277
|
+
if (environment.exists(resolve(targetDir, 'src/integrations', integration, 'layout.tsx'))) {
|
|
302
278
|
integrations.push({
|
|
303
279
|
type: 'layout',
|
|
304
280
|
name: `${integrationName}Layout`,
|
|
305
281
|
path: `integrations/${integration}/layout`,
|
|
306
282
|
});
|
|
307
283
|
}
|
|
308
|
-
if (
|
|
284
|
+
if (environment.exists(resolve(targetDir, 'src/integrations', integration, 'provider.tsx'))) {
|
|
309
285
|
integrations.push({
|
|
310
286
|
type: 'provider',
|
|
311
287
|
name: `${integrationName}Provider`,
|
|
312
288
|
path: `integrations/${integration}/provider`,
|
|
313
289
|
});
|
|
314
290
|
}
|
|
315
|
-
if (
|
|
291
|
+
if (environment.exists(resolve(targetDir, 'src/integrations', integration, 'header-user.tsx'))) {
|
|
316
292
|
integrations.push({
|
|
317
293
|
type: 'header-user',
|
|
318
294
|
name: `${integrationName}Header`,
|
|
@@ -322,8 +298,8 @@ export async function createApp(options, { silent = false, } = {}) {
|
|
|
322
298
|
}
|
|
323
299
|
}
|
|
324
300
|
const routes = [];
|
|
325
|
-
if (
|
|
326
|
-
for (const file of
|
|
301
|
+
if (environment.exists(resolve(targetDir, 'src/routes'))) {
|
|
302
|
+
for (const file of environment.readdir(resolve(targetDir, 'src/routes'))) {
|
|
327
303
|
const name = file.replace(/\.tsx?|\.jsx?/, '');
|
|
328
304
|
const safeRouteName = jsSafeName(name);
|
|
329
305
|
routes.push({
|
|
@@ -374,12 +350,12 @@ export async function createApp(options, { silent = false, } = {}) {
|
|
|
374
350
|
}
|
|
375
351
|
}
|
|
376
352
|
// Add .gitignore
|
|
377
|
-
await copyFile(resolve(templateDirBase, '_dot_gitignore'), resolve(targetDir, '.gitignore'));
|
|
353
|
+
await environment.copyFile(resolve(templateDirBase, '_dot_gitignore'), resolve(targetDir, '.gitignore'));
|
|
378
354
|
// Create the README.md
|
|
379
355
|
await templateFile(templateDirBase, 'README.md.ejs');
|
|
380
356
|
// Install dependencies
|
|
381
357
|
s?.start(`Installing dependencies via ${options.packageManager}...`);
|
|
382
|
-
await
|
|
358
|
+
await environment.execute(options.packageManager, ['install'], targetDir);
|
|
383
359
|
s?.stop(`Installed dependencies`);
|
|
384
360
|
if (warnings.length > 0) {
|
|
385
361
|
if (!silent) {
|
|
@@ -391,23 +367,28 @@ export async function createApp(options, { silent = false, } = {}) {
|
|
|
391
367
|
switch (options.packageManager) {
|
|
392
368
|
case 'pnpm':
|
|
393
369
|
// pnpm automatically forwards extra arguments
|
|
394
|
-
await
|
|
395
|
-
cwd: targetDir,
|
|
396
|
-
});
|
|
370
|
+
await environment.execute(options.packageManager, ['run', 'check', '--fix'], targetDir);
|
|
397
371
|
break;
|
|
398
372
|
default:
|
|
399
|
-
await
|
|
400
|
-
cwd: targetDir,
|
|
401
|
-
});
|
|
373
|
+
await environment.execute(options.packageManager, ['run', 'check', '--', '--fix'], targetDir);
|
|
402
374
|
break;
|
|
403
375
|
}
|
|
404
376
|
s?.stop(`Applied toolchain ${options.toolchain}...`);
|
|
405
377
|
}
|
|
406
378
|
if (options.git) {
|
|
407
379
|
s?.start(`Initializing git repository...`);
|
|
408
|
-
await
|
|
380
|
+
await environment.execute('git', ['init'], targetDir);
|
|
409
381
|
s?.stop(`Initialized git repository`);
|
|
410
382
|
}
|
|
383
|
+
environment.finishRun();
|
|
384
|
+
let errorStatement = '';
|
|
385
|
+
if (environment.getErrors().length) {
|
|
386
|
+
errorStatement = `
|
|
387
|
+
|
|
388
|
+
${chalk.red('There were errors encountered during this process:')}
|
|
389
|
+
|
|
390
|
+
${environment.getErrors().join('\n')}`;
|
|
391
|
+
}
|
|
411
392
|
if (!silent) {
|
|
412
393
|
outro(`Created your new TanStack app in '${basename(targetDir)}'.
|
|
413
394
|
|
|
@@ -415,7 +396,6 @@ Use the following commands to start your app:
|
|
|
415
396
|
% cd ${options.projectName}
|
|
416
397
|
% ${options.packageManager === 'deno' ? 'deno start' : options.packageManager} ${isAddOnEnabled('start') ? 'dev' : 'start'}
|
|
417
398
|
|
|
418
|
-
Please read README.md for more information on testing, styling, adding routes, react-query, etc
|
|
419
|
-
`);
|
|
399
|
+
Please read README.md for more information on testing, styling, adding routes, react-query, etc.${errorStatement}`);
|
|
420
400
|
}
|
|
421
401
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { appendFile, copyFile, mkdir, readFile, writeFile, } from 'node:fs/promises';
|
|
2
|
+
import { existsSync, readdirSync, statSync } from 'node:fs';
|
|
3
|
+
import { dirname } from 'node:path';
|
|
4
|
+
import { execa } from 'execa';
|
|
5
|
+
export function createDefaultEnvironment() {
|
|
6
|
+
let errors = [];
|
|
7
|
+
return {
|
|
8
|
+
startRun: () => {
|
|
9
|
+
errors = [];
|
|
10
|
+
},
|
|
11
|
+
finishRun: () => { },
|
|
12
|
+
getErrors: () => errors,
|
|
13
|
+
appendFile: async (path, contents) => {
|
|
14
|
+
await mkdir(dirname(path), { recursive: true });
|
|
15
|
+
return appendFile(path, contents);
|
|
16
|
+
},
|
|
17
|
+
copyFile: async (from, to) => {
|
|
18
|
+
await mkdir(dirname(to), { recursive: true });
|
|
19
|
+
return copyFile(from, to);
|
|
20
|
+
},
|
|
21
|
+
writeFile: async (path, contents) => {
|
|
22
|
+
await mkdir(dirname(path), { recursive: true });
|
|
23
|
+
return writeFile(path, contents);
|
|
24
|
+
},
|
|
25
|
+
execute: async (command, args, cwd) => {
|
|
26
|
+
try {
|
|
27
|
+
await execa(command, args, {
|
|
28
|
+
cwd,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
errors.push(`Command "${command} ${args.join(' ')}" did not run successfully. Please run this manually in your project.`);
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
readFile: (path, encoding) => readFile(path, { encoding: encoding || 'utf8' }),
|
|
36
|
+
exists: (path) => existsSync(path),
|
|
37
|
+
readdir: (path) => readdirSync(path),
|
|
38
|
+
isDirectory: (path) => {
|
|
39
|
+
const stat = statSync(path);
|
|
40
|
+
return stat.isDirectory();
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
package/dist/mcp.js
CHANGED
|
@@ -5,6 +5,7 @@ import express from 'express';
|
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
import { createApp } from './create-app.js';
|
|
7
7
|
import { finalizeAddOns } from './add-ons.js';
|
|
8
|
+
import { createDefaultEnvironment } from './environment.js';
|
|
8
9
|
const server = new McpServer({
|
|
9
10
|
name: 'Demo',
|
|
10
11
|
version: '1.0.0',
|
|
@@ -93,6 +94,7 @@ server.tool('createTanStackReactApplication', {
|
|
|
93
94
|
variableValues: {},
|
|
94
95
|
}, {
|
|
95
96
|
silent: true,
|
|
97
|
+
environment: createDefaultEnvironment(),
|
|
96
98
|
});
|
|
97
99
|
return {
|
|
98
100
|
content: [{ type: 'text', text: 'Application created successfully' }],
|
|
@@ -170,6 +172,7 @@ server.tool('createTanStackSolidApplication', {
|
|
|
170
172
|
variableValues: {},
|
|
171
173
|
}, {
|
|
172
174
|
silent: true,
|
|
175
|
+
environment: createDefaultEnvironment(),
|
|
173
176
|
});
|
|
174
177
|
return {
|
|
175
178
|
content: [{ type: 'text', text: 'Application created successfully' }],
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-start-app",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"description": "Tanstack Application Builder",
|
|
5
5
|
"bin": "./dist/index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"build": "tsc",
|
|
9
9
|
"start": "tsc && node dist/index.js",
|
|
10
|
-
"test": "npm run test:lint",
|
|
10
|
+
"test": "npm run test:lint && vitest --run",
|
|
11
|
+
"test:watch": "vitest",
|
|
11
12
|
"cipublish": "node scripts/publish.js",
|
|
12
13
|
"test:lint": "eslint ./src",
|
|
13
14
|
"mcp": "tsc && npx @modelcontextprotocol/inspector dist/index.js --mcp"
|
|
@@ -39,6 +40,7 @@
|
|
|
39
40
|
"execa": "^9.5.2",
|
|
40
41
|
"express": "^4.21.2",
|
|
41
42
|
"prettier": "^3.5.0",
|
|
43
|
+
"vitest": "^3.0.8",
|
|
42
44
|
"zod": "^3.24.2"
|
|
43
45
|
},
|
|
44
46
|
"devDependencies": {
|
package/src/cli.ts
CHANGED
|
@@ -10,6 +10,8 @@ import runServer from './mcp.js'
|
|
|
10
10
|
import { listAddOns } from './add-ons.js'
|
|
11
11
|
import { DEFAULT_FRAMEWORK, SUPPORTED_FRAMEWORKS } from './constants.js'
|
|
12
12
|
|
|
13
|
+
import { createDefaultEnvironment } from './environment.js'
|
|
14
|
+
|
|
13
15
|
import type { PackageManager } from './package-manager.js'
|
|
14
16
|
import type { ToolChain } from './toolchain.js'
|
|
15
17
|
import type { CliOptions, Framework } from './types.js'
|
|
@@ -115,7 +117,9 @@ export function cli() {
|
|
|
115
117
|
intro("Let's configure your TanStack application")
|
|
116
118
|
finalOptions = await promptForOptions(cliOptions)
|
|
117
119
|
}
|
|
118
|
-
await createApp(finalOptions
|
|
120
|
+
await createApp(finalOptions, {
|
|
121
|
+
environment: createDefaultEnvironment(),
|
|
122
|
+
})
|
|
119
123
|
} catch (error) {
|
|
120
124
|
log.error(
|
|
121
125
|
error instanceof Error
|