Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/guide/create.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ Run `vp create --list` to see the built-in templates and the common shorthand te
- `--directory <dir>` writes the generated project into a specific target directory
- `--agent <name>` creates agent instructions files during scaffolding
- `--editor <name>` writes editor config files
- `--git` initialize a git repository
- `--no-git` skips git repository initialization
- `--hooks` enables pre-commit hook setup
- `--no-hooks` skips hook setup
- `--no-interactive` runs without prompts
Expand Down
6 changes: 6 additions & 0 deletions packages/cli/snap-tests-global/command-create-help/snap.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Options:
--directory DIR Target directory for the generated project.
--agent NAME Write coding agent instructions to AGENTS.md, CLAUDE.md, etc.
--editor NAME Write editor config files for the specified editor.
--git Initialize a git repository with an initial commit
--no-git Skip git repository initialization
--hooks Set up pre-commit hooks (default in non-interactive mode)
--no-hooks Skip pre-commit hooks setup
--package-manager NAME Use specified package manager (pnpm, npm, yarn, bun)
Expand Down Expand Up @@ -78,6 +80,8 @@ Options:
--directory DIR Target directory for the generated project.
--agent NAME Write coding agent instructions to AGENTS.md, CLAUDE.md, etc.
--editor NAME Write editor config files for the specified editor.
--git Initialize a git repository with an initial commit
--no-git Skip git repository initialization
--hooks Set up pre-commit hooks (default in non-interactive mode)
--no-hooks Skip pre-commit hooks setup
--package-manager NAME Use specified package manager (pnpm, npm, yarn, bun)
Expand Down Expand Up @@ -139,6 +143,8 @@ Options:
--directory DIR Target directory for the generated project.
--agent NAME Write coding agent instructions to AGENTS.md, CLAUDE.md, etc.
--editor NAME Write editor config files for the specified editor.
--git Initialize a git repository with an initial commit
--no-git Skip git repository initialization
--hooks Set up pre-commit hooks (default in non-interactive mode)
--no-hooks Skip pre-commit hooks setup
--package-manager NAME Use specified package manager (pnpm, npm, yarn, bun)
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/snap-tests-global/new-check/snap.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Options:
--directory DIR Target directory for the generated project.
--agent NAME Write coding agent instructions to AGENTS.md, CLAUDE.md, etc.
--editor NAME Write editor config files for the specified editor.
--git Initialize a git repository with an initial commit
--no-git Skip git repository initialization
--hooks Set up pre-commit hooks (default in non-interactive mode)
--no-hooks Skip pre-commit hooks setup
--package-manager NAME Use specified package manager (pnpm, npm, yarn, bun)
Expand Down
30 changes: 29 additions & 1 deletion packages/cli/src/create/bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ import {
writeAgentInstructions,
} from '../utils/agent.ts';
import { detectExistingEditors, selectEditors, writeEditorConfigs } from '../utils/editor.ts';
import { createInitialCommit, initGitRepository } from '../utils/git.ts';
import { renderCliDoc } from '../utils/help.ts';
import { displayRelative } from '../utils/path.ts';
import {
type CommandRunSummary,
defaultInteractive,
downloadPackageManager,
promptGitHooks,
promptGitInit,
runViteFmt,
runViteInstall,
selectPackageManager,
Expand Down Expand Up @@ -106,6 +108,8 @@ const helpMessage = renderCliDoc({
label: '--editor NAME',
description: 'Write editor config files for the specified editor.',
},
{ label: '--git', description: 'Initialize a git repository with an initial commit' },
{ label: '--no-git', description: 'Skip git repository initialization' },
{
label: '--hooks',
description: 'Set up pre-commit hooks (default in non-interactive mode)',
Expand Down Expand Up @@ -235,11 +239,12 @@ function parseArgs() {
verbose?: boolean;
agent?: string | string[] | false;
editor?: string;
git?: boolean;
hooks?: boolean;
'package-manager'?: string;
}>(viteArgs, {
alias: { h: 'help' },
boolean: ['help', 'list', 'all', 'interactive', 'hooks', 'verbose'],
boolean: ['help', 'list', 'all', 'interactive', 'hooks', 'verbose', 'git'],
string: ['directory', 'agent', 'editor', 'package-manager'],
default: { interactive: defaultInteractive() },
});
Expand All @@ -256,6 +261,7 @@ function parseArgs() {
verbose: parsed.verbose || false,
agent: parsed.agent,
editor: parsed.editor,
git: parsed.git,
hooks: parsed.hooks,
packageManager: parsed['package-manager'],
} as Options,
Expand Down Expand Up @@ -747,6 +753,7 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
onCancel: () => cancelAndExit(),
}));

const shouldSetupGit = await promptGitInit(options);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor --no-git for monorepo scaffolds

promptGitInit(options) returns false for vp create vite:monorepo --no-git, but the monorepo-template path still uses its separate shouldInitGit default and calls git init later. In the default compact mode this happens without another prompt, so the newly documented --no-git option does not actually skip repository initialization for monorepo scaffolds.

Useful? React with 👍 / 👎.

if (!isMonorepo) {
shouldSetupHooks = await promptGitHooks(options);
}
Expand Down Expand Up @@ -902,6 +909,10 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
workspaceInfo.rootDir = fullPath;
updateCreateProgress('Integrating monorepo');
rewriteMonorepo(workspaceInfo, undefined, compactOutput);
if (shouldSetupGit) {
updateCreateProgress('Initializing git repository');
await initGitRepository(fullPath);
}
if (bundled?.monorepo) {
// Wire `create.defaultTemplate: '<scope>'` into the new workspace's
// vite.config.ts so a bare `vp create` from inside it opens the
Expand All @@ -922,6 +933,10 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
});
updateCreateProgress('Formatting code');
await runViteFmt(fullPath, options.interactive, undefined, { silent: compactOutput });
if (shouldSetupGit) {
updateCreateProgress('Creating initial commit');
await createInitialCommit(fullPath);
}
clearCreateProgress();
showCreateSummary({
description: describeScaffold(selectedTemplateName, selectedTemplateArgs),
Expand Down Expand Up @@ -1137,6 +1152,11 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
await runViteFmt(workspaceInfo.rootDir, options.interactive, [projectDir], {
silent: compactOutput,
});
if (shouldSetupGit) {
updateCreateProgress('Creating initial commit');
await initGitRepository(workspaceInfo.rootDir);
await createInitialCommit(workspaceInfo.rootDir);
Comment on lines +1157 to +1158
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Limit initial commits to generated files

In an existing monorepo, opting into --git runs the commit from workspaceInfo.rootDir; createInitialCommit does git add -A, which stages every tracked/untracked change in that repo (confirmed via git add -h). If the user has unrelated dirty work anywhere in the monorepo, vp create ... --git will include it in the scaffold's "Initial commit" rather than only the new project/workspace edits.

Useful? React with 👍 / 👎.

Comment on lines +1157 to +1158
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Limit initial commits to generated files

In an existing monorepo, opting into --git runs the commit from workspaceInfo.rootDir; createInitialCommit does git add -A, which stages every tracked/untracked change in that repo. If the user has unrelated dirty work anywhere in the monorepo, vp create ... --git will include it in the scaffold's "Initial commit" rather than only the new project/workspace edits.

Useful? React with 👍 / 👎.

}
} else {
if (shouldMigrateLintFmtTools) {
await installAndMigrate(fullPath);
Expand All @@ -1148,6 +1168,10 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
addFrameworkShim(fullPath, framework);
}
}
if (shouldSetupGit) {
updateCreateProgress('Initializing git repository');
await initGitRepository(fullPath);
}
Comment on lines +1171 to +1174
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add node_modules to .gitignore before standalone commits

For standalone scaffolds, --git initializes the repository before installing dependencies but never calls ensureGitignoreNodeModules; the bundled vite:generator template has no .gitignore, and arbitrary bundled/remote templates can omit one too. In those cases the later git add -A initial commit runs after vp install, so it will commit the generated node_modules directory.

Useful? React with 👍 / 👎.

if (shouldSetupHooks) {
installGitHooks(fullPath, compactOutput);
}
Expand All @@ -1159,6 +1183,10 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
});
updateCreateProgress('Formatting code');
await runViteFmt(fullPath, options.interactive, undefined, { silent: compactOutput });
if (shouldSetupGit) {
updateCreateProgress('Creating initial commit');
await createInitialCommit(fullPath);
Comment on lines +1186 to +1188
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Surface failed initial commits

createInitialCommit returns whether git commit succeeded, but this result is ignored here, so a common failure such as missing user.name/user.email or a failing pre-commit hook leaves the scaffold without the promised initial commit while the command still prints the normal success summary. Check the return value and show the captured git error or fail the command when --git was requested.

Useful? React with 👍 / 👎.

Comment on lines 1185 to +1188
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip committing when setup steps failed

The initial commit is created even when the preceding install or formatter step failed; runViteInstall records status: 'failed' and runViteFmt returns a failed summary, but neither result gates this block. When vp install or vp fmt --write fails for a generated template, --git still commits the broken or partially generated state as the initial commit, making the user unwind that commit before fixing the scaffold.

Useful? React with 👍 / 👎.

}
}

clearCreateProgress();
Expand Down
27 changes: 27 additions & 0 deletions packages/cli/src/utils/git.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { runCommandSilently } from './command.ts';

export async function initGitRepository(cwd: string): Promise<boolean> {
const result = await runCommandSilently({
command: 'git',
args: ['init'],
cwd,
envs: process.env,
});
return result.exitCode === 0;
}

export async function createInitialCommit(cwd: string): Promise<boolean> {
await runCommandSilently({
command: 'git',
args: ['add', '-A'],
cwd,
envs: process.env,
});
const result = await runCommandSilently({
command: 'git',
args: ['commit', '-m', 'Initial commit from Vite+'],
cwd,
envs: process.env,
});
return result.exitCode === 0;
}
24 changes: 24 additions & 0 deletions packages/cli/src/utils/prompts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,30 @@ export async function promptGitHooks(options: {
return true; // non-interactive default
}

export async function promptGitInit(options: {
git?: boolean;
interactive: boolean;
}): Promise<boolean> {
if (options.git === false) {
return false;
}
if (options.git === true) {
return true;
}
if (options.interactive) {
const selected = await prompts.confirm({
message: 'Initialize a git repository with an initial commit?',
initialValue: false,
});
if (prompts.isCancel(selected)) {
cancelAndExit();
return false;
}
return selected;
}
return false; // non-interactive default
}

export function defaultInteractive() {
// If CI environment, use non-interactive mode by default
return !process.env.CI && process.stdin.isTTY;
Expand Down