Skip to content

Commit 8260793

Browse files
committed
feat(create): ask setup questions after the create-xxx CLI runs
1 parent d7d201d commit 8260793

6 files changed

Lines changed: 150 additions & 32 deletions

File tree

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"create-vp-test-generated-setup": {
3+
"name": "create-vp-test-generated-setup",
4+
"dist-tags": {
5+
"latest": "1.0.0"
6+
},
7+
"versions": {
8+
"1.0.0": {
9+
"name": "create-vp-test-generated-setup",
10+
"version": "1.0.0",
11+
"bin": {
12+
"create-vp-test-generated-setup": "index.cjs"
13+
},
14+
"dist": {
15+
"tarball": "{REGISTRY}/create-vp-test-generated-setup/-/create-vp-test-generated-setup-1.0.0.tgz",
16+
"integrity": "sha512-eg95ZfZTfRmqgWms3s+wI5sd8Jl9lMMYWcHsfWtdKKP2hEeIpWUlHAuELDWc/3vyUi8KZDB6edDGwZtIZ2w+Zw=="
17+
}
18+
}
19+
}
20+
}
21+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# The fake `create-vp-test-generated-setup` CLI writes AGENTS.md and
2+
# .vscode/settings.json and runs `git init`, so the setup prompts that follow
3+
# it must react to those files.
4+
[[case]]
5+
name = "create_remote_generated_setup"
6+
vp = "local"
7+
steps = [
8+
{ argv = ["vp", "create", "vp-test-generated-setup", "--package-manager", "pnpm", "--", "my-app"], comment = "after the create CLI: no git prompt, a hooks prompt, then append/skip for the generated AGENTS.md", snapshot = false, continue-on-failure = true, interactions = [
9+
{ "expect-milestone" = "confirm:confirm:yes" },
10+
{ "write-key" = "enter" },
11+
{ "expect-milestone" = "select:select:1" },
12+
{ "write-key" = "up" },
13+
{ "expect-milestone" = "select:select:0" },
14+
{ "write-key" = "enter" },
15+
] },
16+
{ argv = ["vpt", "grep-file", "my-app/AGENTS.md", "<!--VITE PLUS START-->"], comment = "Vite+ instructions are appended to the generated AGENTS.md", continue-on-failure = true },
17+
{ argv = ["vpt", "print-file", "my-app/.vscode/settings.json"], comment = "generated editor config is merged without a prompt", continue-on-failure = true },
18+
{ argv = ["vpt", "stat-file", "my-app/.vite-hooks/pre-commit", "--assert", "file"], comment = "hooks are set up in the repository the create CLI initialized", continue-on-failure = true },
19+
]
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# create_remote_generated_setup
2+
3+
## `vp create vp-test-generated-setup --package-manager pnpm -- my-app`
4+
5+
after the create CLI: no git prompt, a hooks prompt, then append/skip for the generated AGENTS.md
6+
7+
8+
## `vpt grep-file my-app/AGENTS.md '<'\!'--VITE PLUS START-->'`
9+
10+
Vite+ instructions are appended to the generated AGENTS.md
11+
12+
```
13+
my-app/AGENTS.md: found "<!--VITE PLUS START-->"
14+
```
15+
16+
## `vpt print-file my-app/.vscode/settings.json`
17+
18+
generated editor config is merged without a prompt
19+
20+
```
21+
{
22+
"editor.tabSize": 2,
23+
"npm.scriptRunner": "vp",
24+
"editor.defaultFormatter": "oxc.oxc-vscode",
25+
"[javascript]": {
26+
"editor.defaultFormatter": "oxc.oxc-vscode"
27+
},
28+
"[javascriptreact]": {
29+
"editor.defaultFormatter": "oxc.oxc-vscode"
30+
},
31+
"[typescript]": {
32+
"editor.defaultFormatter": "oxc.oxc-vscode"
33+
},
34+
"[typescriptreact]": {
35+
"editor.defaultFormatter": "oxc.oxc-vscode"
36+
},
37+
"oxc.disableNestedConfig": true,
38+
"oxc.fmt.disableNestedConfig": true,
39+
"editor.formatOnSave": true,
40+
"editor.formatOnSaveMode": "file",
41+
"editor.codeActionsOnSave": {
42+
"source.fixAll.oxc": "explicit"
43+
}
44+
}
45+
```
46+
47+
## `vpt stat-file my-app/.vite-hooks/pre-commit --assert file`
48+
49+
hooks are set up in the repository the create CLI initialized
50+
51+
```
52+
my-app/.vite-hooks/pre-commit: file
53+
```

‎docs/guide/create.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ Vite+ ships with these built-in templates:
7474

7575
Run `vp create --list` to see the built-in templates and the common shorthand templates Vite+ recognizes.
7676

77+
A third-party template runs right after the package manager is chosen. `vp create` asks about agent instructions, editor configs, git, and commit hooks once the template finishes, based on the files it generated: an existing agent instructions file can be appended to or kept, existing editor configs are merged, and an existing git repository is reused.
78+
7779
After a third-party template runs, `vp create` migrates the generated project to Vite+. When a standalone project already depends on `vite-plus`, `vp create` skips this migration and keeps the template's tooling choices.
7880

7981
## Options

‎packages/cli/src/create/bin.ts‎

Lines changed: 55 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -732,45 +732,64 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
732732
? formatLocalVpCommand(packageManager, 'vp run')
733733
: 'vp run';
734734

735-
const existingAgentTargetPaths =
736-
options.agent !== undefined || !options.interactive
737-
? undefined
738-
: detectExistingAgentTargetPaths(workspaceInfoOptional.rootDir);
739-
if (existingAgentTargetPaths !== undefined) {
740-
selectedAgentTargetPaths = existingAgentTargetPaths;
741-
} else {
742-
const agentSelection = await selectAgentTargets({
743-
interactive: options.interactive,
744-
agent: options.agent,
745-
onCancel: () => cancelAndExit(),
746-
});
747-
selectedAgentTargetPaths = agentSelection.targetPaths;
748-
shouldWriteCopilotSetupWorkflow = agentSelection.selectedAgents.some(
749-
(agent) => agent.id === COPILOT_AGENT_ID,
750-
);
751-
}
752-
753735
const shouldConfigureEditors = shouldConfigureEditorsForCreate({
754736
editor: options.editor,
755737
isMonorepo,
756738
});
757-
if (shouldConfigureEditors) {
758-
const existingEditors =
759-
options.editor || !options.interactive
739+
740+
// Agent, editor, git and hook choices. Direct scaffolds ask up front. A
741+
// `create-xxx` CLI asks after it ran (`generatedProjectPath`), so its own
742+
// prompts come first and the answers reflect the files it generated.
743+
let shouldSetupGit = false;
744+
const resolveProjectSetup = async (generatedProjectPath?: string) => {
745+
const agentInstructionsRoot =
746+
generatedProjectPath && !isMonorepo ? generatedProjectPath : workspaceInfoOptional.rootDir;
747+
const existingAgentTargetPaths =
748+
options.agent !== undefined || !options.interactive
760749
? undefined
761-
: detectExistingEditors(workspaceInfoOptional.rootDir);
762-
selectedEditors =
763-
existingEditors ??
764-
(await selectEditors({
750+
: detectExistingAgentTargetPaths(agentInstructionsRoot);
751+
if (existingAgentTargetPaths !== undefined) {
752+
selectedAgentTargetPaths = existingAgentTargetPaths;
753+
} else {
754+
const agentSelection = await selectAgentTargets({
765755
interactive: options.interactive,
766-
editor: options.editor,
756+
agent: options.agent,
767757
onCancel: () => cancelAndExit(),
768-
}));
769-
}
758+
});
759+
selectedAgentTargetPaths = agentSelection.targetPaths;
760+
shouldWriteCopilotSetupWorkflow = agentSelection.selectedAgents.some(
761+
(agent) => agent.id === COPILOT_AGENT_ID,
762+
);
763+
}
764+
765+
if (shouldConfigureEditors) {
766+
const existingEditors =
767+
options.editor || !options.interactive
768+
? undefined
769+
: detectExistingEditors(generatedProjectPath ?? workspaceInfoOptional.rootDir);
770+
selectedEditors =
771+
existingEditors ??
772+
(await selectEditors({
773+
interactive: options.interactive,
774+
editor: options.editor,
775+
onCancel: () => cancelAndExit(),
776+
}));
777+
}
770778

771-
const shouldSetupGit = await resolveGitInit(options, isMonorepo);
772-
if (!isMonorepo && (!options.interactive || shouldSetupGit || options.hooks === true)) {
773-
shouldSetupHooks = await promptGitHooks(options);
779+
// A `create-xxx` CLI may have initialized the repository itself.
780+
const hasGitRepository =
781+
generatedProjectPath !== undefined && fs.existsSync(path.join(generatedProjectPath, '.git'));
782+
shouldSetupGit = !hasGitRepository && (await resolveGitInit(options, isMonorepo));
783+
if (
784+
!isMonorepo &&
785+
(!options.interactive || hasGitRepository || shouldSetupGit || options.hooks === true)
786+
) {
787+
shouldSetupHooks = await promptGitHooks(options);
788+
}
789+
};
790+
791+
if (isDirectScaffoldTemplate) {
792+
await resolveProjectSetup();
774793
}
775794

776795
const createProgress =
@@ -1079,6 +1098,9 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
10791098
}
10801099

10811100
const fullPath = path.join(workspaceInfo.rootDir, projectDir);
1101+
if (!isDirectScaffoldTemplate) {
1102+
await resolveProjectSetup(fullPath);
1103+
}
10821104

10831105
// Register a scaffolded generator in `create.templates` so it appears in the
10841106
// `vp create` picker (and resolves by name) without a manual config edit.
@@ -1140,7 +1162,8 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
11401162
await writeEditorConfigs({
11411163
projectRoot: fullPath,
11421164
editorId: selectedEditors,
1143-
interactive: options.interactive,
1165+
// Editor configs a `create-xxx` CLI generated are merged without asking per file.
1166+
interactive: options.interactive && isDirectScaffoldTemplate,
11441167
silent: compactOutput,
11451168
extraVsCodeSettings: { 'npm.scriptRunner': 'vp' },
11461169
packageManager,

0 commit comments

Comments
 (0)