Rails-inspired generator system that provides scaffolding for your apps
Git utilities for user information and repository operations through simple-git integration with support for configuration access and GitHub username resolution.
Access git user configuration for author information and defaults.
/**
* Git utilities interface
*/
interface GitUtil {
/**
* Retrieves user's name from Git in the global scope or project scope
* Takes what Git will use in the current context
* @returns Promise resolving to configured git name or undefined
*/
name(): Promise<string | undefined>;
/**
* Retrieves user's email from Git in the global scope or project scope
* Takes what Git will use in the current context
* @returns Promise resolving to configured git email or undefined
*/
email(): Promise<string | undefined>;
}
/**
* Git utilities instance
*/
readonly git: GitUtil;Usage Examples:
export default class MyGenerator extends Generator {
async prompting() {
// Get git user info for prompt defaults
const gitName = await this.git.name();
const gitEmail = await this.git.email();
this.answers = await this.prompt([
{
name: 'authorName',
message: 'Author name:',
default: gitName || 'Anonymous',
store: true
},
{
name: 'authorEmail',
message: 'Author email:',
default: gitEmail || '',
store: true
}
]);
}
async configuring() {
// Use git info in package.json
const author = {
name: this.answers.authorName || await this.git.name(),
email: this.answers.authorEmail || await this.git.email()
};
this.packageJson.merge({ author });
}
async initializing() {
// Validate git configuration
const gitName = await this.git.name();
const gitEmail = await this.git.email();
if (!gitName || !gitEmail) {
this.log('Warning: Git user.name and user.email should be configured');
this.log('Run: git config --global user.name "Your Name"');
this.log('Run: git config --global user.email "your.email@example.com"');
}
}
}Direct access to simple-git instance for advanced git operations.
/**
* Simple-git instance for advanced git operations
* Configured with current destination root and locale settings
*/
readonly simpleGit: SimpleGit;Usage Examples:
import { SimpleGit } from 'simple-git';
export default class MyGenerator extends Generator {
async writing() {
// Initialize git repository
if (this.answers.initGit && !await this.isGitRepo()) {
await this.simpleGit.init();
this.log('Initialized git repository');
}
// Set up git configuration for this project
if (this.answers.gitConfig) {
await this.simpleGit.addConfig('user.name', this.answers.authorName);
await this.simpleGit.addConfig('user.email', this.answers.authorEmail);
}
// Add gitignore
this.renderTemplate('gitignore.ejs', '.gitignore', {
nodeModules: true,
buildDirs: ['dist/', 'build/'],
logFiles: ['*.log', 'logs/']
});
}
async install() {
// Create initial commit after installation
if (this.answers.initialCommit) {
await this.simpleGit.add('.');
await this.simpleGit.commit('Initial commit\n\n🤖 Generated with Yeoman');
this.log('Created initial commit');
}
}
async end() {
// Set up remote repository
if (this.answers.remoteUrl) {
await this.simpleGit.addRemote('origin', this.answers.remoteUrl);
if (this.answers.pushToRemote) {
await this.simpleGit.push('origin', 'main');
this.log(`Pushed to remote: ${this.answers.remoteUrl}`);
}
}
}
// Helper method to check if directory is a git repo
async isGitRepo(): Promise<boolean> {
try {
await this.simpleGit.status();
return true;
} catch (error) {
return false;
}
}
}GitHub utilities for username resolution (deprecated, will be removed in v8).
/**
* @deprecated Will be removed in version 8
* GitHub utilities
*/
readonly github: {
/**
* @deprecated Will be removed in version 8. Use 'github-username' package with await this.git.email() result instead
* Retrieves GitHub's username from the GitHub API using email
* @returns Promise resolving to GitHub username or undefined
*/
username(): Promise<string | undefined>;
};Usage Example:
export default class MyGenerator extends Generator {
async prompting() {
// Get GitHub username (deprecated - use alternative approach)
let githubUsername;
try {
// Deprecated approach
githubUsername = await this.github.username();
} catch (error) {
// Alternative approach using email
const email = await this.git.email();
if (email) {
// Manual username derivation or use github-username package
githubUsername = email.split('@')[0];
}
}
this.answers = await this.prompt([
{
name: 'githubUsername',
message: 'GitHub username:',
default: githubUsername,
when: this.answers.publishToGithub
}
]);
}
}Complex git workflows and repository management.
Usage Examples:
export default class MyGenerator extends Generator {
async initializing() {
// Check git status and working directory
if (await this.isGitRepo()) {
const status = await this.simpleGit.status();
if (!status.isClean()) {
this.log('Warning: Working directory is not clean');
this.log('Uncommitted changes may be overwritten');
const proceed = await this.prompt({
type: 'confirm',
name: 'continueWithDirtyRepo',
message: 'Continue anyway?',
default: false
});
if (!proceed.continueWithDirtyRepo) {
throw new Error('Aborted due to uncommitted changes');
}
}
}
}
async writing() {
// Create branch for generated code
if (this.answers.createBranch) {
const branchName = `generator/${this.answers.name}`;
try {
await this.simpleGit.checkoutLocalBranch(branchName);
this.log(`Created and switched to branch: ${branchName}`);
} catch (error) {
this.log(`Branch ${branchName} may already exist, switching to it`);
await this.simpleGit.checkout(branchName);
}
}
// Set up git hooks
if (this.answers.setupHooks) {
this.renderTemplate('pre-commit.ejs', '.git/hooks/pre-commit', {
runLint: this.answers.useLinting,
runTests: this.answers.useTesting
});
// Make hook executable
await this.spawn('chmod', ['+x', '.git/hooks/pre-commit']);
}
}
async end() {
// Tag the generated version
if (this.answers.createTag) {
const tagName = `v${this.packageJson.get('version')}`;
await this.simpleGit.addTag(tagName);
this.log(`Created tag: ${tagName}`);
}
// Generate changelog from commits
if (this.answers.generateChangelog) {
const log = await this.simpleGit.log(['--oneline', '--decorate']);
const changelog = log.all.map(commit =>
`- ${commit.message} (${commit.hash.substring(0, 7)})`
).join('\n');
this.writeDestination('CHANGELOG.md', `# Changelog\n\n${changelog}\n`);
}
}
}Validate git setup and configure repository settings.
Usage Examples:
export default class MyGenerator extends Generator {
async initializing() {
// Comprehensive git validation
await this.validateGitSetup();
}
async validateGitSetup() {
// Check if git is available
try {
await this.spawnCommand('git --version', { stdio: 'pipe' });
} catch (error) {
throw new Error('Git is not installed or not available in PATH');
}
// Check git configuration
const gitName = await this.git.name();
const gitEmail = await this.git.email();
if (!gitName) {
this.log('⚠️ Git user.name is not configured');
this.log('Run: git config --global user.name "Your Name"');
}
if (!gitEmail) {
this.log('⚠️ Git user.email is not configured');
this.log('Run: git config --global user.email "your.email@example.com"');
}
// Check if we're in a git repository
const isRepo = await this.isGitRepo();
if (!isRepo && this.answers.requireGitRepo) {
throw new Error('This generator requires a git repository. Run "git init" first.');
}
return { gitName, gitEmail, isRepo };
}
async configuring() {
// Configure repository-specific settings
if (this.answers.configureGit) {
// Set repository-specific configuration
await this.simpleGit.addConfig('core.autocrlf', 'input');
await this.simpleGit.addConfig('pull.rebase', 'false');
// Configure commit template
if (this.answers.useCommitTemplate) {
this.writeDestination('.gitmessage', `
# Title: Summary, imperative, start upper case, don't end with a period
# No more than 50 chars. #### 50 chars is here: #
# Remember blank line between title and body.
# Body: Explain *what* and *why* (not *how*). Include task ID (Jira issue).
# Wrap at 72 chars. ################################## which is here: #
# At the end: Include Co-authored-by for all contributors.
# Include at least one empty line before it. Format:
# Co-authored-by: name <user@users.noreply.github.com>
#
# How to Write a Git Commit Message:
# https://chris.beams.io/posts/git-commit/
#
# 1. Separate subject from body with a blank line
# 2. Limit the subject line to 50 characters
# 3. Capitalize the subject line
# 4. Do not end the subject line with a period
# 5. Use the imperative mood in the subject line
# 6. Wrap the body at 72 characters
# 7. Use the body to explain what and why vs. how
`.trim());
await this.simpleGit.addConfig('commit.template', '.gitmessage');
}
}
}
async isGitRepo(): Promise<boolean> {
try {
await this.simpleGit.status();
return true;
} catch (error) {
return false;
}
}
}interface GitUtil {
name(): Promise<string | undefined>;
email(): Promise<string | undefined>;
}
// Simple-git types (from simple-git package)
interface SimpleGit {
init(): Promise<void>;
status(): Promise<StatusResult>;
add(files: string | string[]): Promise<void>;
commit(message: string): Promise<CommitResult>;
push(remote?: string, branch?: string): Promise<void>;
addRemote(remoteName: string, remoteUrl: string): Promise<void>;
addConfig(key: string, value: string): Promise<void>;
getConfig(key: string): Promise<ConfigGetResult>;
addTag(tagName: string): Promise<void>;
log(options?: string[]): Promise<LogResult>;
checkout(branch: string): Promise<void>;
checkoutLocalBranch(branchName: string): Promise<void>;
}Install with Tessl CLI
npx tessl i tessl/npm-yeoman-generator