@tanstack/cli 0.59.8 → 0.60.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/CHANGELOG.md +18 -0
- package/dist/bin.js +5 -0
- package/dist/cli.js +118 -93
- package/dist/command-line.js +143 -8
- package/dist/dev-watch.js +117 -16
- package/dist/file-syncer.js +30 -1
- package/dist/index.js +15 -1
- package/dist/options.js +5 -2
- package/dist/types/cli.d.ts +1 -2
- package/dist/types/dev-watch.d.ts +6 -0
- package/dist/types/file-syncer.d.ts +8 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/types.d.ts +2 -1
- package/package.json +8 -3
- package/playwright-report/index.html +85 -0
- package/playwright.config.ts +21 -0
- package/src/bin.ts +8 -0
- package/src/cli.ts +150 -119
- package/src/command-line.ts +193 -7
- package/src/dev-watch.ts +163 -29
- package/src/file-syncer.ts +59 -1
- package/src/index.ts +21 -1
- package/src/options.ts +8 -2
- package/src/types.ts +2 -1
- package/test-results/.last-run.json +4 -0
- package/tests/command-line.test.ts +203 -15
- package/tests/options.test.ts +2 -2
- package/tests-e2e/addons-smoke.spec.ts +31 -0
- package/tests-e2e/create-smoke.spec.ts +39 -0
- package/tests-e2e/helpers.ts +526 -0
- package/tests-e2e/matrix-opportunistic.spec.ts +142 -0
- package/tests-e2e/router-only-smoke.spec.ts +68 -0
- package/tests-e2e/solid-smoke.spec.ts +25 -0
- package/tests-e2e/templates-smoke.spec.ts +52 -0
- package/vitest.config.js +1 -0
package/dist/dev-watch.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
3
4
|
import chokidar from 'chokidar';
|
|
4
5
|
import chalk from 'chalk';
|
|
5
6
|
import { temporaryDirectory } from 'tempy';
|
|
6
|
-
import { createApp, getFrameworkById, registerFramework, } from '@tanstack/create';
|
|
7
|
+
import { createApp, finalizeAddOns, getFrameworkById, registerFramework, scanAddOnDirectories, scanProjectDirectory, } from '@tanstack/create';
|
|
7
8
|
import { FileSyncer } from './file-syncer.js';
|
|
8
9
|
import { createUIEnvironment } from './ui-environment.js';
|
|
9
10
|
class DebounceQueue {
|
|
@@ -42,6 +43,8 @@ export class DevWatchManager {
|
|
|
42
43
|
this.tempDir = null;
|
|
43
44
|
this.isBuilding = false;
|
|
44
45
|
this.buildCount = 0;
|
|
46
|
+
this.appDevProcess = null;
|
|
47
|
+
this.lastSyncedSourceFiles = null;
|
|
45
48
|
this.log = {
|
|
46
49
|
tree: (prefix, msg, isLast = false) => {
|
|
47
50
|
const connector = isLast ? '└─' : '├─';
|
|
@@ -78,12 +81,18 @@ export class DevWatchManager {
|
|
|
78
81
|
console.log(chalk.bold('dev-watch'));
|
|
79
82
|
this.log.tree('', `watching: ${chalk.cyan(this.options.watchPath)}`);
|
|
80
83
|
this.log.tree('', `target: ${chalk.cyan(this.options.targetDir)}`);
|
|
84
|
+
if (this.options.runDevCommand) {
|
|
85
|
+
this.log.tree('', `app dev server: ${chalk.cyan('enabled')}`);
|
|
86
|
+
}
|
|
81
87
|
this.log.tree('', 'ready', true);
|
|
82
88
|
// Setup signal handlers
|
|
83
89
|
process.on('SIGINT', () => this.cleanup());
|
|
84
90
|
process.on('SIGTERM', () => this.cleanup());
|
|
85
91
|
// Start watching
|
|
86
92
|
this.startWatcher();
|
|
93
|
+
if (this.options.runDevCommand) {
|
|
94
|
+
this.startAppDevServer();
|
|
95
|
+
}
|
|
87
96
|
}
|
|
88
97
|
async stop() {
|
|
89
98
|
console.log();
|
|
@@ -145,17 +154,16 @@ export class DevWatchManager {
|
|
|
145
154
|
try {
|
|
146
155
|
this.log.section(`build #${buildId}`);
|
|
147
156
|
const startTime = Date.now();
|
|
148
|
-
|
|
149
|
-
|
|
157
|
+
let refreshedFramework = this.createFrameworkDefinitionFromWatchPath();
|
|
158
|
+
if (!refreshedFramework && this.options.frameworkDefinitionInitializers) {
|
|
159
|
+
const refreshedFrameworks = this.options.frameworkDefinitionInitializers.map((frameworkInitalizer) => frameworkInitalizer());
|
|
160
|
+
refreshedFramework = refreshedFrameworks.find((f) => f.id === this.options.framework.id);
|
|
150
161
|
}
|
|
151
|
-
const refreshedFrameworks = this.options.frameworkDefinitionInitializers.map((frameworkInitalizer) => frameworkInitalizer());
|
|
152
|
-
const refreshedFramework = refreshedFrameworks.find((f) => f.id === this.options.framework.id);
|
|
153
162
|
if (!refreshedFramework) {
|
|
154
|
-
throw new Error('Could not
|
|
163
|
+
throw new Error('Could not refresh framework from watch path or framework initializers');
|
|
155
164
|
}
|
|
156
165
|
// Update the chosen addons to use the latest code
|
|
157
166
|
const chosenAddonIds = this.options.cliOptions.chosenAddOns.map((m) => m.id);
|
|
158
|
-
const updatedChosenAddons = refreshedFramework.addOns.filter((f) => chosenAddonIds.includes(f.id));
|
|
159
167
|
// Create temp directory for this build using tempy
|
|
160
168
|
this.tempDir = temporaryDirectory();
|
|
161
169
|
// Register the scanned framework
|
|
@@ -168,25 +176,33 @@ export class DevWatchManager {
|
|
|
168
176
|
if (!registeredFramework) {
|
|
169
177
|
throw new Error(`Failed to register framework: ${this.options.framework.id}`);
|
|
170
178
|
}
|
|
171
|
-
|
|
172
|
-
|
|
179
|
+
const updatedChosenAddons = await finalizeAddOns(registeredFramework, this.options.cliOptions.mode, chosenAddonIds);
|
|
180
|
+
// Check if package metadata was modified
|
|
181
|
+
const packageMetadataChanged = Array.from(changes).some((filePath) => {
|
|
182
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
183
|
+
return /(^|\/)package\.json(\.ejs)?$/.test(normalized);
|
|
184
|
+
});
|
|
173
185
|
const updatedOptions = {
|
|
174
186
|
...this.options.cliOptions,
|
|
175
187
|
chosenAddOns: updatedChosenAddons,
|
|
176
188
|
framework: registeredFramework,
|
|
177
189
|
targetDir: this.tempDir,
|
|
178
190
|
git: false,
|
|
179
|
-
install:
|
|
191
|
+
install: packageMetadataChanged,
|
|
180
192
|
};
|
|
181
193
|
// Show package installation indicator if needed
|
|
182
|
-
if (
|
|
194
|
+
if (packageMetadataChanged) {
|
|
183
195
|
this.log.tree(' ', `${chalk.yellow('⟳')} installing packages...`);
|
|
184
196
|
}
|
|
185
197
|
// Create app in temp directory with silent environment
|
|
186
198
|
const silentEnvironment = createUIEnvironment(this.options.environment.appName, true);
|
|
187
199
|
await createApp(silentEnvironment, updatedOptions);
|
|
188
200
|
// Sync files to target directory
|
|
189
|
-
const syncResult = await this.syncer.sync(this.tempDir, this.options.targetDir
|
|
201
|
+
const syncResult = await this.syncer.sync(this.tempDir, this.options.targetDir, {
|
|
202
|
+
deleteRemoved: this.lastSyncedSourceFiles !== null,
|
|
203
|
+
previousSourceFiles: this.lastSyncedSourceFiles ?? undefined,
|
|
204
|
+
});
|
|
205
|
+
this.lastSyncedSourceFiles = new Set(syncResult.sourceFiles);
|
|
190
206
|
// Clean up temp directory after sync is complete
|
|
191
207
|
try {
|
|
192
208
|
await fs.promises.rm(this.tempDir, { recursive: true, force: true });
|
|
@@ -197,18 +213,24 @@ export class DevWatchManager {
|
|
|
197
213
|
const elapsed = Date.now() - startTime;
|
|
198
214
|
// Build tree-style summary
|
|
199
215
|
this.log.tree(' ', `duration: ${chalk.cyan(elapsed + 'ms')}`);
|
|
200
|
-
if (
|
|
216
|
+
if (packageMetadataChanged) {
|
|
201
217
|
this.log.tree(' ', `packages: ${chalk.green('✓ installed')}`);
|
|
202
218
|
}
|
|
203
219
|
// Always show the last item in tree without checking for files to show
|
|
204
220
|
const noMoreTreeItems = syncResult.updated.length === 0 &&
|
|
205
221
|
syncResult.created.length === 0 &&
|
|
222
|
+
syncResult.deleted.length === 0 &&
|
|
206
223
|
syncResult.errors.length === 0;
|
|
207
224
|
if (syncResult.updated.length > 0) {
|
|
208
|
-
this.log.tree(' ', `updated: ${chalk.green(syncResult.updated.length + ' file' + (syncResult.updated.length > 1 ? 's' : ''))}`, syncResult.created.length === 0 &&
|
|
225
|
+
this.log.tree(' ', `updated: ${chalk.green(syncResult.updated.length + ' file' + (syncResult.updated.length > 1 ? 's' : ''))}`, syncResult.created.length === 0 &&
|
|
226
|
+
syncResult.deleted.length === 0 &&
|
|
227
|
+
syncResult.errors.length === 0);
|
|
209
228
|
}
|
|
210
229
|
if (syncResult.created.length > 0) {
|
|
211
|
-
this.log.tree(' ', `created: ${chalk.green(syncResult.created.length + ' file' + (syncResult.created.length > 1 ? 's' : ''))}`, syncResult.errors.length === 0);
|
|
230
|
+
this.log.tree(' ', `created: ${chalk.green(syncResult.created.length + ' file' + (syncResult.created.length > 1 ? 's' : ''))}`, syncResult.deleted.length === 0 && syncResult.errors.length === 0);
|
|
231
|
+
}
|
|
232
|
+
if (syncResult.deleted.length > 0) {
|
|
233
|
+
this.log.tree(' ', `deleted: ${chalk.green(syncResult.deleted.length + ' file' + (syncResult.deleted.length > 1 ? 's' : ''))}`, syncResult.errors.length === 0);
|
|
212
234
|
}
|
|
213
235
|
if (syncResult.errors.length > 0) {
|
|
214
236
|
this.log.tree(' ', `failed: ${chalk.red(syncResult.errors.length + ' file' + (syncResult.errors.length > 1 ? 's' : ''))}`, true);
|
|
@@ -254,10 +276,16 @@ export class DevWatchManager {
|
|
|
254
276
|
// Show created files
|
|
255
277
|
if (syncResult.created.length > 0) {
|
|
256
278
|
syncResult.created.forEach((file, index) => {
|
|
257
|
-
const isLast = index === syncResult.created.length - 1;
|
|
279
|
+
const isLast = index === syncResult.created.length - 1 && syncResult.deleted.length === 0;
|
|
258
280
|
this.log.treeItem(' ', `${chalk.green('+')} ${file}`, isLast);
|
|
259
281
|
});
|
|
260
282
|
}
|
|
283
|
+
if (syncResult.deleted.length > 0) {
|
|
284
|
+
syncResult.deleted.forEach((file, index) => {
|
|
285
|
+
const isLast = index === syncResult.deleted.length - 1;
|
|
286
|
+
this.log.treeItem(' ', `${chalk.red('-')} ${file}`, isLast);
|
|
287
|
+
});
|
|
288
|
+
}
|
|
261
289
|
// Always show errors
|
|
262
290
|
if (syncResult.errors.length > 0) {
|
|
263
291
|
console.log(); // Add spacing
|
|
@@ -273,6 +301,34 @@ export class DevWatchManager {
|
|
|
273
301
|
this.isBuilding = false;
|
|
274
302
|
}
|
|
275
303
|
}
|
|
304
|
+
createFrameworkDefinitionFromWatchPath() {
|
|
305
|
+
const frameworkRoot = this.options.watchPath;
|
|
306
|
+
const projectDirectory = path.join(frameworkRoot, 'project');
|
|
307
|
+
const baseDirectory = path.join(projectDirectory, 'base');
|
|
308
|
+
if (!fs.existsSync(projectDirectory) || !fs.existsSync(baseDirectory)) {
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
const addOnDirectoryCandidates = [
|
|
312
|
+
path.join(frameworkRoot, 'add-ons'),
|
|
313
|
+
path.join(frameworkRoot, 'toolchains'),
|
|
314
|
+
path.join(frameworkRoot, 'examples'),
|
|
315
|
+
path.join(frameworkRoot, 'hosts'),
|
|
316
|
+
];
|
|
317
|
+
const addOnDirectories = addOnDirectoryCandidates.filter((dir) => fs.existsSync(dir));
|
|
318
|
+
const addOns = addOnDirectories.length > 0 ? scanAddOnDirectories(addOnDirectories) : [];
|
|
319
|
+
const { files, basePackageJSON, optionalPackages } = scanProjectDirectory(projectDirectory, baseDirectory);
|
|
320
|
+
return {
|
|
321
|
+
id: this.options.framework.id,
|
|
322
|
+
name: this.options.framework.name,
|
|
323
|
+
description: this.options.framework.description,
|
|
324
|
+
version: this.options.framework.version,
|
|
325
|
+
base: files,
|
|
326
|
+
addOns,
|
|
327
|
+
basePackageJSON,
|
|
328
|
+
optionalPackages,
|
|
329
|
+
supportedModes: this.options.framework.supportedModes,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
276
332
|
cleanup() {
|
|
277
333
|
console.log();
|
|
278
334
|
console.log('Cleaning up...');
|
|
@@ -285,6 +341,51 @@ export class DevWatchManager {
|
|
|
285
341
|
this.log.error(`Failed to clean up temp directory: ${error instanceof Error ? error.message : String(error)}`);
|
|
286
342
|
}
|
|
287
343
|
}
|
|
344
|
+
if (this.appDevProcess && !this.appDevProcess.killed) {
|
|
345
|
+
this.appDevProcess.kill('SIGTERM');
|
|
346
|
+
this.appDevProcess = null;
|
|
347
|
+
}
|
|
288
348
|
process.exit(0);
|
|
289
349
|
}
|
|
350
|
+
startAppDevServer() {
|
|
351
|
+
if (this.appDevProcess) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
const { command, args } = this.getDevCommandForPackageManager(this.options.packageManager);
|
|
355
|
+
this.log.section('app dev server');
|
|
356
|
+
this.log.tree(' ', `starting: ${chalk.cyan([command, ...args].join(' '))}`);
|
|
357
|
+
this.appDevProcess = spawn(command, args, {
|
|
358
|
+
cwd: this.options.targetDir,
|
|
359
|
+
stdio: 'inherit',
|
|
360
|
+
shell: process.platform === 'win32',
|
|
361
|
+
env: process.env,
|
|
362
|
+
});
|
|
363
|
+
this.appDevProcess.on('exit', (code, signal) => {
|
|
364
|
+
if (signal) {
|
|
365
|
+
this.log.warning(`app dev server exited via signal ${signal}`);
|
|
366
|
+
}
|
|
367
|
+
else if (code && code !== 0) {
|
|
368
|
+
this.log.warning(`app dev server exited with code ${code}`);
|
|
369
|
+
}
|
|
370
|
+
this.appDevProcess = null;
|
|
371
|
+
});
|
|
372
|
+
this.appDevProcess.on('error', (error) => {
|
|
373
|
+
this.log.error(`Failed to start app dev server: ${error.message}`);
|
|
374
|
+
this.appDevProcess = null;
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
getDevCommandForPackageManager(packageManager) {
|
|
378
|
+
switch (packageManager) {
|
|
379
|
+
case 'npm':
|
|
380
|
+
return { command: 'npm', args: ['run', 'dev'] };
|
|
381
|
+
case 'yarn':
|
|
382
|
+
return { command: 'yarn', args: ['dev'] };
|
|
383
|
+
case 'bun':
|
|
384
|
+
return { command: 'bun', args: ['run', 'dev'] };
|
|
385
|
+
case 'deno':
|
|
386
|
+
return { command: 'deno', args: ['task', 'dev'] };
|
|
387
|
+
default:
|
|
388
|
+
return { command: 'pnpm', args: ['dev'] };
|
|
389
|
+
}
|
|
390
|
+
}
|
|
290
391
|
}
|
package/dist/file-syncer.js
CHANGED
|
@@ -3,11 +3,13 @@ import path from 'node:path';
|
|
|
3
3
|
import crypto from 'node:crypto';
|
|
4
4
|
import * as diff from 'diff';
|
|
5
5
|
export class FileSyncer {
|
|
6
|
-
async sync(sourceDir, targetDir) {
|
|
6
|
+
async sync(sourceDir, targetDir, options) {
|
|
7
7
|
const result = {
|
|
8
8
|
updated: [],
|
|
9
9
|
skipped: [],
|
|
10
10
|
created: [],
|
|
11
|
+
deleted: [],
|
|
12
|
+
sourceFiles: [],
|
|
11
13
|
errors: [],
|
|
12
14
|
};
|
|
13
15
|
// Ensure directories exist
|
|
@@ -19,6 +21,10 @@ export class FileSyncer {
|
|
|
19
21
|
}
|
|
20
22
|
// Walk through source directory and sync files
|
|
21
23
|
await this.syncDirectory(sourceDir, targetDir, sourceDir, result);
|
|
24
|
+
if (options?.deleteRemoved && options.previousSourceFiles) {
|
|
25
|
+
const currentSourceFileSet = new Set(result.sourceFiles);
|
|
26
|
+
await this.deleteRemovedFiles(targetDir, options.previousSourceFiles, currentSourceFileSet, result);
|
|
27
|
+
}
|
|
22
28
|
return result;
|
|
23
29
|
}
|
|
24
30
|
async syncDirectory(currentPath, targetBase, sourceBase, result) {
|
|
@@ -46,6 +52,7 @@ export class FileSyncer {
|
|
|
46
52
|
if (this.shouldSkipFile(entry.name)) {
|
|
47
53
|
continue;
|
|
48
54
|
}
|
|
55
|
+
result.sourceFiles.push(relativePath);
|
|
49
56
|
try {
|
|
50
57
|
const shouldUpdate = await this.shouldUpdateFile(sourcePath, targetPath);
|
|
51
58
|
if (shouldUpdate) {
|
|
@@ -145,4 +152,26 @@ export class FileSyncer {
|
|
|
145
152
|
const ext = path.extname(name).toLowerCase();
|
|
146
153
|
return skipExtensions.includes(ext);
|
|
147
154
|
}
|
|
155
|
+
async deleteRemovedFiles(targetDir, previousSourceFiles, currentSourceFiles, result) {
|
|
156
|
+
for (const relativePath of previousSourceFiles) {
|
|
157
|
+
if (currentSourceFiles.has(relativePath)) {
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const targetPath = path.join(targetDir, relativePath);
|
|
161
|
+
try {
|
|
162
|
+
if (!fs.existsSync(targetPath)) {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const stats = await fs.promises.stat(targetPath);
|
|
166
|
+
if (!stats.isFile()) {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
await fs.promises.unlink(targetPath);
|
|
170
|
+
result.deleted.push(relativePath);
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
result.errors.push(`${relativePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
148
177
|
}
|
package/dist/index.js
CHANGED
|
@@ -1 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
import { pathToFileURL } from 'node:url';
|
|
2
|
+
import { createReactFrameworkDefinition, createSolidFrameworkDefinition, } from '@tanstack/create';
|
|
3
|
+
import { cli } from './cli.js';
|
|
4
|
+
export { cli };
|
|
5
|
+
const entryPath = process.argv[1];
|
|
6
|
+
if (entryPath && import.meta.url === pathToFileURL(entryPath).href) {
|
|
7
|
+
cli({
|
|
8
|
+
name: 'tanstack',
|
|
9
|
+
appName: 'TanStack',
|
|
10
|
+
frameworkDefinitionInitializers: [
|
|
11
|
+
createReactFrameworkDefinition,
|
|
12
|
+
createSolidFrameworkDefinition,
|
|
13
|
+
],
|
|
14
|
+
});
|
|
15
|
+
}
|
package/dist/options.js
CHANGED
|
@@ -4,7 +4,7 @@ import { getProjectName, promptForAddOnOptions, promptForEnvVars, selectAddOns,
|
|
|
4
4
|
import { getCurrentDirectoryName, sanitizePackageName, validateProjectName, } from './utils.js';
|
|
5
5
|
export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], showDeploymentOptions = false, }) {
|
|
6
6
|
const options = {};
|
|
7
|
-
options.framework = getFrameworkById(cliOptions.framework || 'react
|
|
7
|
+
options.framework = getFrameworkById(cliOptions.framework || 'react');
|
|
8
8
|
// Validate project name
|
|
9
9
|
if (cliOptions.projectName) {
|
|
10
10
|
// Handle "." as project name - use sanitized current directory name
|
|
@@ -26,7 +26,10 @@ export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], sh
|
|
|
26
26
|
// Mode is always file-router (TanStack Start)
|
|
27
27
|
options.mode = 'file-router';
|
|
28
28
|
const template = cliOptions.template?.toLowerCase().trim();
|
|
29
|
-
const
|
|
29
|
+
const isLegacyTemplate = template &&
|
|
30
|
+
['file-router', 'typescript', 'tsx', 'javascript', 'js', 'jsx'].includes(template);
|
|
31
|
+
const routerOnly = !!cliOptions.routerOnly ||
|
|
32
|
+
(isLegacyTemplate ? template !== 'file-router' : false);
|
|
30
33
|
// TypeScript is always enabled with file-router
|
|
31
34
|
options.typescript = true;
|
|
32
35
|
// Package manager selection
|
package/dist/types/cli.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import type { FrameworkDefinition } from '@tanstack/create';
|
|
2
|
-
export declare function cli({ name, appName, forcedAddOns, forcedDeployment, defaultFramework,
|
|
2
|
+
export declare function cli({ name, appName, forcedAddOns, forcedDeployment, defaultFramework, frameworkDefinitionInitializers, showDeploymentOptions, legacyAutoCreate, defaultRouterOnly, }: {
|
|
3
3
|
name: string;
|
|
4
4
|
appName: string;
|
|
5
5
|
forcedAddOns?: Array<string>;
|
|
6
6
|
forcedDeployment?: string;
|
|
7
7
|
defaultFramework?: string;
|
|
8
|
-
webBase?: string;
|
|
9
8
|
frameworkDefinitionInitializers?: Array<() => FrameworkDefinition>;
|
|
10
9
|
showDeploymentOptions?: boolean;
|
|
11
10
|
legacyAutoCreate?: boolean;
|
|
@@ -5,6 +5,7 @@ export interface DevWatchOptions {
|
|
|
5
5
|
framework: Framework;
|
|
6
6
|
cliOptions: Options;
|
|
7
7
|
packageManager: string;
|
|
8
|
+
runDevCommand?: boolean;
|
|
8
9
|
environment: Environment;
|
|
9
10
|
frameworkDefinitionInitializers?: Array<() => FrameworkDefinition>;
|
|
10
11
|
}
|
|
@@ -16,12 +17,17 @@ export declare class DevWatchManager {
|
|
|
16
17
|
private tempDir;
|
|
17
18
|
private isBuilding;
|
|
18
19
|
private buildCount;
|
|
20
|
+
private appDevProcess;
|
|
21
|
+
private lastSyncedSourceFiles;
|
|
19
22
|
constructor(options: DevWatchOptions);
|
|
20
23
|
start(): Promise<void>;
|
|
21
24
|
stop(): Promise<void>;
|
|
22
25
|
private startWatcher;
|
|
23
26
|
private handleChange;
|
|
24
27
|
private rebuild;
|
|
28
|
+
private createFrameworkDefinitionFromWatchPath;
|
|
25
29
|
private cleanup;
|
|
30
|
+
private startAppDevServer;
|
|
31
|
+
private getDevCommandForPackageManager;
|
|
26
32
|
private log;
|
|
27
33
|
}
|
|
@@ -6,13 +6,20 @@ export interface SyncResult {
|
|
|
6
6
|
updated: Array<FileUpdate>;
|
|
7
7
|
skipped: Array<string>;
|
|
8
8
|
created: Array<string>;
|
|
9
|
+
deleted: Array<string>;
|
|
10
|
+
sourceFiles: Array<string>;
|
|
9
11
|
errors: Array<string>;
|
|
10
12
|
}
|
|
13
|
+
export interface SyncOptions {
|
|
14
|
+
deleteRemoved?: boolean;
|
|
15
|
+
previousSourceFiles?: Set<string>;
|
|
16
|
+
}
|
|
11
17
|
export declare class FileSyncer {
|
|
12
|
-
sync(sourceDir: string, targetDir: string): Promise<SyncResult>;
|
|
18
|
+
sync(sourceDir: string, targetDir: string, options?: SyncOptions): Promise<SyncResult>;
|
|
13
19
|
private syncDirectory;
|
|
14
20
|
private shouldUpdateFile;
|
|
15
21
|
private calculateHash;
|
|
16
22
|
private shouldSkipDirectory;
|
|
17
23
|
private shouldSkipFile;
|
|
24
|
+
private deleteRemovedFiles;
|
|
18
25
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import { cli } from './cli.js';
|
|
2
|
+
export { cli };
|
package/dist/types/types.d.ts
CHANGED
|
@@ -12,10 +12,11 @@ export interface CliOptions {
|
|
|
12
12
|
mcp?: boolean;
|
|
13
13
|
mcpSse?: boolean;
|
|
14
14
|
starter?: string;
|
|
15
|
+
templateId?: string;
|
|
15
16
|
targetDir?: string;
|
|
16
17
|
interactive?: boolean;
|
|
17
|
-
ui?: boolean;
|
|
18
18
|
devWatch?: string;
|
|
19
|
+
runDev?: boolean;
|
|
19
20
|
install?: boolean;
|
|
20
21
|
addOnConfig?: string;
|
|
21
22
|
force?: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.60.0",
|
|
4
4
|
"description": "TanStack CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -38,10 +38,10 @@
|
|
|
38
38
|
"tempy": "^3.1.0",
|
|
39
39
|
"validate-npm-package-name": "^7.0.0",
|
|
40
40
|
"zod": "^3.24.2",
|
|
41
|
-
"@tanstack/create": "0.
|
|
42
|
-
"@tanstack/create-ui": "0.59.8"
|
|
41
|
+
"@tanstack/create": "0.62.0"
|
|
43
42
|
},
|
|
44
43
|
"devDependencies": {
|
|
44
|
+
"@playwright/test": "^1.58.2",
|
|
45
45
|
"@tanstack/config": "^0.16.2",
|
|
46
46
|
"@types/diff": "^5.2.0",
|
|
47
47
|
"@types/express": "^5.0.1",
|
|
@@ -57,6 +57,11 @@
|
|
|
57
57
|
"scripts": {
|
|
58
58
|
"build": "tsc",
|
|
59
59
|
"dev": "tsc --watch",
|
|
60
|
+
"test:e2e": "npm run test:e2e:blocking",
|
|
61
|
+
"test:e2e:blocking": "npm run build && playwright test --grep @blocking",
|
|
62
|
+
"test:e2e:matrix": "npm run build && playwright test --grep @matrix",
|
|
63
|
+
"test:e2e:debug": "npm run build && playwright test --debug",
|
|
64
|
+
"test:e2e:headed": "npm run build && playwright test --headed",
|
|
60
65
|
"test:lint": "eslint ./src",
|
|
61
66
|
"test": "vitest run",
|
|
62
67
|
"test:watch": "vitest",
|