@tanstack/cli 0.59.6 → 0.59.7
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 +21 -0
- package/dist/cli.js +10 -2
- package/dist/command-line.js +11 -2
- package/dist/options.js +19 -4
- package/dist/types/types.d.ts +1 -0
- package/dist/types/ui-prompts.d.ts +3 -1
- package/dist/ui-prompts.js +54 -1
- package/package.json +3 -3
- package/src/cli.ts +11 -0
- package/src/command-line.ts +13 -2
- package/src/options.ts +30 -10
- package/src/types.ts +1 -0
- package/src/ui-prompts.ts +76 -0
- package/tests/command-line.test.ts +17 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @tanstack/cli
|
|
2
2
|
|
|
3
|
+
## 0.59.7
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Add a continuous development workflow for custom add-on authors. ([`b3cc585`](https://github.com/TanStack/cli/commit/b3cc5851d2b81613e3b024eb7981c440ee5183af))
|
|
8
|
+
|
|
9
|
+
- Add `tanstack add-on dev` to watch project files and continuously refresh `.add-on` outputs.
|
|
10
|
+
- Rebuild `.add-on` assets and `add-on.json` automatically when source files change.
|
|
11
|
+
- Document the new add-on development loop in the custom add-on guide.
|
|
12
|
+
|
|
13
|
+
- Improve scaffold customization and custom add-on authoring flow. ([`5fbf262`](https://github.com/TanStack/cli/commit/5fbf262fe3a0d070e6a78fa2f2a920b176b84480))
|
|
14
|
+
|
|
15
|
+
- Add `--examples` / `--no-examples` support to include or omit demo/example pages during app creation.
|
|
16
|
+
- Prompt for add-on-declared environment variables during interactive create and seed entered values into generated `.env.local`.
|
|
17
|
+
- Ensure custom add-on/starter metadata consistently includes a `version`, with safe backfill for older metadata files.
|
|
18
|
+
- Align bundled starter/example metadata and docs with current Start/file-router behavior.
|
|
19
|
+
|
|
20
|
+
- Updated dependencies [[`b3cc585`](https://github.com/TanStack/cli/commit/b3cc5851d2b81613e3b024eb7981c440ee5183af), [`5fbf262`](https://github.com/TanStack/cli/commit/5fbf262fe3a0d070e6a78fa2f2a920b176b84480)]:
|
|
21
|
+
- @tanstack/create@0.61.5
|
|
22
|
+
- @tanstack/create-ui@0.59.7
|
|
23
|
+
|
|
3
24
|
## 0.59.6
|
|
4
25
|
|
|
5
26
|
### Patch Changes
|
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import { Command, InvalidArgumentError } from 'commander';
|
|
|
4
4
|
import { cancel, confirm, intro, isCancel, log } from '@clack/prompts';
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
import semver from 'semver';
|
|
7
|
-
import { SUPPORTED_PACKAGE_MANAGERS, addToApp, compileAddOn, compileStarter, createApp, createSerializedOptions, getAllAddOns, getFrameworkByName, getFrameworks, initAddOn, initStarter, } from '@tanstack/create';
|
|
7
|
+
import { SUPPORTED_PACKAGE_MANAGERS, addToApp, compileAddOn, devAddOn, compileStarter, createApp, createSerializedOptions, getAllAddOns, getFrameworkByName, getFrameworks, initAddOn, initStarter, } from '@tanstack/create';
|
|
8
8
|
import { launchUI } from '@tanstack/create-ui';
|
|
9
9
|
import { runMCPServer } from './mcp.js';
|
|
10
10
|
import { promptForAddOns, promptForCreateOptions } from './options.js';
|
|
@@ -300,7 +300,9 @@ export function cli({ name, appName, forcedAddOns = [], forcedDeployment, defaul
|
|
|
300
300
|
.option('--router-only', 'Use router-only compatibility mode (file-based routing without TanStack Start)')
|
|
301
301
|
.option('--template <type>', 'Deprecated: compatibility flag from create-tsrouter-app')
|
|
302
302
|
.option('--tailwind', 'Deprecated: compatibility flag; Tailwind is always enabled')
|
|
303
|
-
.option('--no-tailwind', 'Deprecated: compatibility flag; Tailwind opt-out is ignored')
|
|
303
|
+
.option('--no-tailwind', 'Deprecated: compatibility flag; Tailwind opt-out is ignored')
|
|
304
|
+
.option('--examples', 'include demo/example pages')
|
|
305
|
+
.option('--no-examples', 'exclude demo/example pages');
|
|
304
306
|
if (deployments.size > 0) {
|
|
305
307
|
cmd.option(`--deployment <${Array.from(deployments).join('|')}>`, `Explicitly tell the CLI to use this deployment adapter`, (value) => {
|
|
306
308
|
if (!deployments.has(value)) {
|
|
@@ -473,6 +475,12 @@ Remove your node_modules directory and package lock file and re-install.`);
|
|
|
473
475
|
.action(async () => {
|
|
474
476
|
await compileAddOn(environment);
|
|
475
477
|
});
|
|
478
|
+
addOnCommand
|
|
479
|
+
.command('dev')
|
|
480
|
+
.description('Watch project files and continuously refresh .add-on and add-on.json')
|
|
481
|
+
.action(async () => {
|
|
482
|
+
await devAddOn(environment);
|
|
483
|
+
});
|
|
476
484
|
// === STARTER SUBCOMMAND ===
|
|
477
485
|
const starterCommand = program.command('starter');
|
|
478
486
|
starterCommand
|
package/dist/command-line.js
CHANGED
|
@@ -123,7 +123,11 @@ export async function normalizeOptions(cliOptions, forcedAddOns, opts) {
|
|
|
123
123
|
}
|
|
124
124
|
return [];
|
|
125
125
|
}
|
|
126
|
-
const
|
|
126
|
+
const includeExamples = cliOptions.examples ?? !routerOnly;
|
|
127
|
+
const chosenAddOnsRaw = await selectAddOns();
|
|
128
|
+
const chosenAddOns = includeExamples
|
|
129
|
+
? chosenAddOnsRaw
|
|
130
|
+
: chosenAddOnsRaw.filter((addOn) => addOn.type !== 'example');
|
|
127
131
|
// Handle add-on configuration option
|
|
128
132
|
let addOnOptionsFromCLI = {};
|
|
129
133
|
if (cliOptions.addOnConfig) {
|
|
@@ -135,7 +139,7 @@ export async function normalizeOptions(cliOptions, forcedAddOns, opts) {
|
|
|
135
139
|
process.exit(1);
|
|
136
140
|
}
|
|
137
141
|
}
|
|
138
|
-
|
|
142
|
+
const normalized = {
|
|
139
143
|
projectName: projectName,
|
|
140
144
|
targetDir,
|
|
141
145
|
framework,
|
|
@@ -154,6 +158,11 @@ export async function normalizeOptions(cliOptions, forcedAddOns, opts) {
|
|
|
154
158
|
},
|
|
155
159
|
starter: starter,
|
|
156
160
|
};
|
|
161
|
+
normalized.includeExamples =
|
|
162
|
+
includeExamples;
|
|
163
|
+
normalized.envVarValues =
|
|
164
|
+
{};
|
|
165
|
+
return normalized;
|
|
157
166
|
}
|
|
158
167
|
export function validateDevWatchOptions(cliOptions) {
|
|
159
168
|
if (!cliOptions.devWatch) {
|
package/dist/options.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { intro } from '@clack/prompts';
|
|
2
2
|
import { finalizeAddOns, getFrameworkById, getPackageManager, populateAddOnOptionsDefaults, readConfigFile, } from '@tanstack/create';
|
|
3
|
-
import { getProjectName, promptForAddOnOptions, selectAddOns, selectDeployment, selectGit, selectPackageManager, selectToolchain, } from './ui-prompts.js';
|
|
3
|
+
import { getProjectName, promptForAddOnOptions, promptForEnvVars, selectAddOns, selectDeployment, selectExamples, selectGit, selectPackageManager, selectToolchain, } from './ui-prompts.js';
|
|
4
4
|
import { getCurrentDirectoryName, sanitizePackageName, validateProjectName, } from './utils.js';
|
|
5
5
|
export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], showDeploymentOptions = false, }) {
|
|
6
6
|
const options = {};
|
|
@@ -48,6 +48,10 @@ export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], sh
|
|
|
48
48
|
: undefined;
|
|
49
49
|
// Add-ons selection
|
|
50
50
|
const addOns = new Set();
|
|
51
|
+
// Examples/demo pages are enabled by default
|
|
52
|
+
const includeExamples = cliOptions.examples ?? (routerOnly ? false : await selectExamples());
|
|
53
|
+
options.includeExamples =
|
|
54
|
+
includeExamples;
|
|
51
55
|
if (toolchain) {
|
|
52
56
|
addOns.add(toolchain);
|
|
53
57
|
}
|
|
@@ -71,11 +75,16 @@ export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], sh
|
|
|
71
75
|
for (const addOn of await selectAddOns(options.framework, options.mode, 'add-on', 'What add-ons would you like for your project?', forcedAddOns)) {
|
|
72
76
|
addOns.add(addOn);
|
|
73
77
|
}
|
|
74
|
-
|
|
75
|
-
|
|
78
|
+
if (includeExamples) {
|
|
79
|
+
for (const addOn of await selectAddOns(options.framework, options.mode, 'example', 'Would you like an example?', forcedAddOns, false)) {
|
|
80
|
+
addOns.add(addOn);
|
|
81
|
+
}
|
|
76
82
|
}
|
|
77
83
|
}
|
|
78
|
-
|
|
84
|
+
const chosenAddOns = Array.from(await finalizeAddOns(options.framework, options.mode, Array.from(addOns)));
|
|
85
|
+
options.chosenAddOns = includeExamples
|
|
86
|
+
? chosenAddOns
|
|
87
|
+
: chosenAddOns.filter((addOn) => addOn.type !== 'example');
|
|
79
88
|
// Tailwind is always enabled
|
|
80
89
|
options.tailwind = true;
|
|
81
90
|
// Prompt for add-on options in interactive mode
|
|
@@ -90,6 +99,12 @@ export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], sh
|
|
|
90
99
|
// Merge user options with defaults
|
|
91
100
|
options.addOnOptions = { ...defaultOptions, ...userOptions };
|
|
92
101
|
}
|
|
102
|
+
// Prompt for env vars exposed by selected add-ons in interactive mode
|
|
103
|
+
const envVarValues = Array.isArray(cliOptions.addOns)
|
|
104
|
+
? {}
|
|
105
|
+
: await promptForEnvVars(options.chosenAddOns);
|
|
106
|
+
options.envVarValues =
|
|
107
|
+
envVarValues;
|
|
93
108
|
options.git = cliOptions.git ?? (await selectGit());
|
|
94
109
|
if (cliOptions.install === false) {
|
|
95
110
|
options.install = false;
|
package/dist/types/types.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import type { PackageManager } from '@tanstack/create';
|
|
1
|
+
import type { AddOn, PackageManager } from '@tanstack/create';
|
|
2
2
|
import type { Framework } from '@tanstack/create/dist/types/types.js';
|
|
3
3
|
export declare function getProjectName(): Promise<string>;
|
|
4
4
|
export declare function selectPackageManager(): Promise<PackageManager>;
|
|
5
5
|
export declare function selectAddOns(framework: Framework, mode: string, type: string, message: string, forcedAddOns?: Array<string>, allowMultiple?: boolean): Promise<Array<string>>;
|
|
6
6
|
export declare function selectGit(): Promise<boolean>;
|
|
7
|
+
export declare function selectExamples(): Promise<boolean>;
|
|
7
8
|
export declare function selectToolchain(framework: Framework, toolchain?: string | false): Promise<string | undefined>;
|
|
8
9
|
export declare function promptForAddOnOptions(addOnIds: Array<string>, framework: Framework): Promise<Record<string, Record<string, any>>>;
|
|
10
|
+
export declare function promptForEnvVars(addOns: Array<AddOn>): Promise<Record<string, string>>;
|
|
9
11
|
export declare function selectDeployment(framework: Framework, deployment?: string): Promise<string | undefined>;
|
package/dist/ui-prompts.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { cancel, confirm, isCancel, multiselect, note, select, text, } from '@clack/prompts';
|
|
1
|
+
import { cancel, confirm, isCancel, multiselect, note, password, select, text, } from '@clack/prompts';
|
|
2
2
|
import { DEFAULT_PACKAGE_MANAGER, SUPPORTED_PACKAGE_MANAGERS, getAllAddOns, } from '@tanstack/create';
|
|
3
3
|
import { validateProjectName } from './utils.js';
|
|
4
4
|
export async function getProjectName() {
|
|
@@ -106,6 +106,17 @@ export async function selectGit() {
|
|
|
106
106
|
}
|
|
107
107
|
return git;
|
|
108
108
|
}
|
|
109
|
+
export async function selectExamples() {
|
|
110
|
+
const includeExamples = await confirm({
|
|
111
|
+
message: 'Would you like to include demo/example pages?',
|
|
112
|
+
initialValue: true,
|
|
113
|
+
});
|
|
114
|
+
if (isCancel(includeExamples)) {
|
|
115
|
+
cancel('Operation cancelled.');
|
|
116
|
+
process.exit(0);
|
|
117
|
+
}
|
|
118
|
+
return includeExamples;
|
|
119
|
+
}
|
|
109
120
|
export async function selectToolchain(framework, toolchain) {
|
|
110
121
|
if (toolchain === false) {
|
|
111
122
|
return undefined;
|
|
@@ -170,6 +181,48 @@ export async function promptForAddOnOptions(addOnIds, framework) {
|
|
|
170
181
|
}
|
|
171
182
|
return addOnOptions;
|
|
172
183
|
}
|
|
184
|
+
export async function promptForEnvVars(addOns) {
|
|
185
|
+
const envVars = new Map();
|
|
186
|
+
for (const addOn of addOns) {
|
|
187
|
+
for (const envVar of addOn.envVars || []) {
|
|
188
|
+
if (!envVars.has(envVar.name)) {
|
|
189
|
+
envVars.set(envVar.name, envVar);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const result = {};
|
|
194
|
+
for (const envVar of envVars.values()) {
|
|
195
|
+
const label = envVar.description
|
|
196
|
+
? `${envVar.name} (${envVar.description})`
|
|
197
|
+
: envVar.name;
|
|
198
|
+
const value = envVar.secret
|
|
199
|
+
? await password({
|
|
200
|
+
message: `Enter ${label}`,
|
|
201
|
+
validate: envVar.required
|
|
202
|
+
? (v) => v && v.trim().length > 0
|
|
203
|
+
? undefined
|
|
204
|
+
: `${envVar.name} is required`
|
|
205
|
+
: undefined,
|
|
206
|
+
})
|
|
207
|
+
: await text({
|
|
208
|
+
message: `Enter ${label}`,
|
|
209
|
+
defaultValue: envVar.default,
|
|
210
|
+
validate: envVar.required
|
|
211
|
+
? (v) => v && v.trim().length > 0
|
|
212
|
+
? undefined
|
|
213
|
+
: `${envVar.name} is required`
|
|
214
|
+
: undefined,
|
|
215
|
+
});
|
|
216
|
+
if (isCancel(value)) {
|
|
217
|
+
cancel('Operation cancelled.');
|
|
218
|
+
process.exit(0);
|
|
219
|
+
}
|
|
220
|
+
if (value && value.trim()) {
|
|
221
|
+
result[envVar.name] = value.trim();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
173
226
|
export async function selectDeployment(framework, deployment) {
|
|
174
227
|
const deployments = new Set();
|
|
175
228
|
let initialValue = undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/cli",
|
|
3
|
-
"version": "0.59.
|
|
3
|
+
"version": "0.59.7",
|
|
4
4
|
"description": "TanStack CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -38,8 +38,8 @@
|
|
|
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.61.
|
|
42
|
-
"@tanstack/create-ui": "0.59.
|
|
41
|
+
"@tanstack/create": "0.61.5",
|
|
42
|
+
"@tanstack/create-ui": "0.59.7"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@tanstack/config": "^0.16.2",
|
package/src/cli.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
SUPPORTED_PACKAGE_MANAGERS,
|
|
10
10
|
addToApp,
|
|
11
11
|
compileAddOn,
|
|
12
|
+
devAddOn,
|
|
12
13
|
compileStarter,
|
|
13
14
|
createApp,
|
|
14
15
|
createSerializedOptions,
|
|
@@ -466,6 +467,8 @@ export function cli({
|
|
|
466
467
|
'--no-tailwind',
|
|
467
468
|
'Deprecated: compatibility flag; Tailwind opt-out is ignored',
|
|
468
469
|
)
|
|
470
|
+
.option('--examples', 'include demo/example pages')
|
|
471
|
+
.option('--no-examples', 'exclude demo/example pages')
|
|
469
472
|
|
|
470
473
|
if (deployments.size > 0) {
|
|
471
474
|
cmd.option<string>(
|
|
@@ -695,6 +698,14 @@ Remove your node_modules directory and package lock file and re-install.`,
|
|
|
695
698
|
.action(async () => {
|
|
696
699
|
await compileAddOn(environment)
|
|
697
700
|
})
|
|
701
|
+
addOnCommand
|
|
702
|
+
.command('dev')
|
|
703
|
+
.description(
|
|
704
|
+
'Watch project files and continuously refresh .add-on and add-on.json',
|
|
705
|
+
)
|
|
706
|
+
.action(async () => {
|
|
707
|
+
await devAddOn(environment)
|
|
708
|
+
})
|
|
698
709
|
|
|
699
710
|
// === STARTER SUBCOMMAND ===
|
|
700
711
|
const starterCommand = program.command('starter')
|
package/src/command-line.ts
CHANGED
|
@@ -191,7 +191,11 @@ export async function normalizeOptions(
|
|
|
191
191
|
return []
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
-
const
|
|
194
|
+
const includeExamples = cliOptions.examples ?? !routerOnly
|
|
195
|
+
const chosenAddOnsRaw = await selectAddOns()
|
|
196
|
+
const chosenAddOns = includeExamples
|
|
197
|
+
? chosenAddOnsRaw
|
|
198
|
+
: chosenAddOnsRaw.filter((addOn) => addOn.type !== 'example')
|
|
195
199
|
|
|
196
200
|
// Handle add-on configuration option
|
|
197
201
|
let addOnOptionsFromCLI = {}
|
|
@@ -204,7 +208,7 @@ export async function normalizeOptions(
|
|
|
204
208
|
}
|
|
205
209
|
}
|
|
206
210
|
|
|
207
|
-
|
|
211
|
+
const normalized = {
|
|
208
212
|
projectName: projectName,
|
|
209
213
|
targetDir,
|
|
210
214
|
framework,
|
|
@@ -224,6 +228,13 @@ export async function normalizeOptions(
|
|
|
224
228
|
},
|
|
225
229
|
starter: starter,
|
|
226
230
|
}
|
|
231
|
+
|
|
232
|
+
;(normalized as Options & { includeExamples?: boolean }).includeExamples =
|
|
233
|
+
includeExamples
|
|
234
|
+
;(normalized as Options & { envVarValues?: Record<string, string> }).envVarValues =
|
|
235
|
+
{}
|
|
236
|
+
|
|
237
|
+
return normalized
|
|
227
238
|
}
|
|
228
239
|
|
|
229
240
|
export function validateDevWatchOptions(cliOptions: CliOptions): {
|
package/src/options.ts
CHANGED
|
@@ -11,8 +11,10 @@ import {
|
|
|
11
11
|
import {
|
|
12
12
|
getProjectName,
|
|
13
13
|
promptForAddOnOptions,
|
|
14
|
+
promptForEnvVars,
|
|
14
15
|
selectAddOns,
|
|
15
16
|
selectDeployment,
|
|
17
|
+
selectExamples,
|
|
16
18
|
selectGit,
|
|
17
19
|
selectPackageManager,
|
|
18
20
|
selectToolchain,
|
|
@@ -92,6 +94,12 @@ export async function promptForCreateOptions(
|
|
|
92
94
|
// Add-ons selection
|
|
93
95
|
const addOns: Set<string> = new Set()
|
|
94
96
|
|
|
97
|
+
// Examples/demo pages are enabled by default
|
|
98
|
+
const includeExamples =
|
|
99
|
+
cliOptions.examples ?? (routerOnly ? false : await selectExamples())
|
|
100
|
+
;(options as Required<Options> & { includeExamples?: boolean }).includeExamples =
|
|
101
|
+
includeExamples
|
|
102
|
+
|
|
95
103
|
if (toolchain) {
|
|
96
104
|
addOns.add(toolchain)
|
|
97
105
|
}
|
|
@@ -123,21 +131,26 @@ export async function promptForCreateOptions(
|
|
|
123
131
|
addOns.add(addOn)
|
|
124
132
|
}
|
|
125
133
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
134
|
+
if (includeExamples) {
|
|
135
|
+
for (const addOn of await selectAddOns(
|
|
136
|
+
options.framework,
|
|
137
|
+
options.mode,
|
|
138
|
+
'example',
|
|
139
|
+
'Would you like an example?',
|
|
140
|
+
forcedAddOns,
|
|
141
|
+
false,
|
|
142
|
+
)) {
|
|
143
|
+
addOns.add(addOn)
|
|
144
|
+
}
|
|
135
145
|
}
|
|
136
146
|
}
|
|
137
147
|
|
|
138
|
-
|
|
148
|
+
const chosenAddOns = Array.from(
|
|
139
149
|
await finalizeAddOns(options.framework, options.mode, Array.from(addOns)),
|
|
140
150
|
)
|
|
151
|
+
options.chosenAddOns = includeExamples
|
|
152
|
+
? chosenAddOns
|
|
153
|
+
: chosenAddOns.filter((addOn) => addOn.type !== 'example')
|
|
141
154
|
|
|
142
155
|
// Tailwind is always enabled
|
|
143
156
|
options.tailwind = true
|
|
@@ -157,6 +170,13 @@ export async function promptForCreateOptions(
|
|
|
157
170
|
options.addOnOptions = { ...defaultOptions, ...userOptions }
|
|
158
171
|
}
|
|
159
172
|
|
|
173
|
+
// Prompt for env vars exposed by selected add-ons in interactive mode
|
|
174
|
+
const envVarValues = Array.isArray(cliOptions.addOns)
|
|
175
|
+
? {}
|
|
176
|
+
: await promptForEnvVars(options.chosenAddOns)
|
|
177
|
+
;(options as Required<Options> & { envVarValues?: Record<string, string> }).envVarValues =
|
|
178
|
+
envVarValues
|
|
179
|
+
|
|
160
180
|
options.git = cliOptions.git ?? (await selectGit())
|
|
161
181
|
if (cliOptions.install === false) {
|
|
162
182
|
options.install = false
|
package/src/types.ts
CHANGED
package/src/ui-prompts.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
isCancel,
|
|
5
5
|
multiselect,
|
|
6
6
|
note,
|
|
7
|
+
password,
|
|
7
8
|
select,
|
|
8
9
|
text,
|
|
9
10
|
} from '@clack/prompts'
|
|
@@ -151,6 +152,18 @@ export async function selectGit(): Promise<boolean> {
|
|
|
151
152
|
return git
|
|
152
153
|
}
|
|
153
154
|
|
|
155
|
+
export async function selectExamples(): Promise<boolean> {
|
|
156
|
+
const includeExamples = await confirm({
|
|
157
|
+
message: 'Would you like to include demo/example pages?',
|
|
158
|
+
initialValue: true,
|
|
159
|
+
})
|
|
160
|
+
if (isCancel(includeExamples)) {
|
|
161
|
+
cancel('Operation cancelled.')
|
|
162
|
+
process.exit(0)
|
|
163
|
+
}
|
|
164
|
+
return includeExamples
|
|
165
|
+
}
|
|
166
|
+
|
|
154
167
|
export async function selectToolchain(
|
|
155
168
|
framework: Framework,
|
|
156
169
|
toolchain?: string | false,
|
|
@@ -239,6 +252,69 @@ export async function promptForAddOnOptions(
|
|
|
239
252
|
return addOnOptions
|
|
240
253
|
}
|
|
241
254
|
|
|
255
|
+
export async function promptForEnvVars(
|
|
256
|
+
addOns: Array<AddOn>,
|
|
257
|
+
): Promise<Record<string, string>> {
|
|
258
|
+
const envVars = new Map<
|
|
259
|
+
string,
|
|
260
|
+
{
|
|
261
|
+
name: string
|
|
262
|
+
description?: string
|
|
263
|
+
required?: boolean
|
|
264
|
+
default?: string
|
|
265
|
+
secret?: boolean
|
|
266
|
+
}
|
|
267
|
+
>()
|
|
268
|
+
|
|
269
|
+
for (const addOn of addOns as Array<any>) {
|
|
270
|
+
for (const envVar of addOn.envVars || []) {
|
|
271
|
+
if (!envVars.has(envVar.name)) {
|
|
272
|
+
envVars.set(envVar.name, envVar)
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const result: Record<string, string> = {}
|
|
278
|
+
|
|
279
|
+
for (const envVar of envVars.values()) {
|
|
280
|
+
const label = envVar.description
|
|
281
|
+
? `${envVar.name} (${envVar.description})`
|
|
282
|
+
: envVar.name
|
|
283
|
+
|
|
284
|
+
const value = envVar.secret
|
|
285
|
+
? await password({
|
|
286
|
+
message: `Enter ${label}`,
|
|
287
|
+
validate: envVar.required
|
|
288
|
+
? (v) =>
|
|
289
|
+
v && v.trim().length > 0
|
|
290
|
+
? undefined
|
|
291
|
+
: `${envVar.name} is required`
|
|
292
|
+
: undefined,
|
|
293
|
+
})
|
|
294
|
+
: await text({
|
|
295
|
+
message: `Enter ${label}`,
|
|
296
|
+
defaultValue: envVar.default,
|
|
297
|
+
validate: envVar.required
|
|
298
|
+
? (v) =>
|
|
299
|
+
v && v.trim().length > 0
|
|
300
|
+
? undefined
|
|
301
|
+
: `${envVar.name} is required`
|
|
302
|
+
: undefined,
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
if (isCancel(value)) {
|
|
306
|
+
cancel('Operation cancelled.')
|
|
307
|
+
process.exit(0)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (value && value.trim()) {
|
|
311
|
+
result[envVar.name] = value.trim()
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return result
|
|
316
|
+
}
|
|
317
|
+
|
|
242
318
|
export async function selectDeployment(
|
|
243
319
|
framework: Framework,
|
|
244
320
|
deployment?: string,
|
|
@@ -300,6 +300,23 @@ describe('normalizeOptions', () => {
|
|
|
300
300
|
expect(options?.mode).toBe('file-router')
|
|
301
301
|
})
|
|
302
302
|
|
|
303
|
+
it('includes examples by default in non-router-only mode', async () => {
|
|
304
|
+
const options = await normalizeOptions({
|
|
305
|
+
projectName: 'test',
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
expect((options as any)?.includeExamples).toBe(true)
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
it('supports disabling examples from the CLI', async () => {
|
|
312
|
+
const options = await normalizeOptions({
|
|
313
|
+
projectName: 'test',
|
|
314
|
+
examples: false,
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
expect((options as any)?.includeExamples).toBe(false)
|
|
318
|
+
})
|
|
319
|
+
|
|
303
320
|
it('should ignore add-ons and deployment in router-only mode but keep toolchain', async () => {
|
|
304
321
|
__testRegisterFramework({
|
|
305
322
|
id: 'react-cra',
|