browser-plugin-creator
Version:
A modern scaffolding tool for creating browser extensions with ease
142 lines (141 loc) • 5.8 kB
JavaScript
import path from 'path';
import fs from 'fs-extra';
import { execSync } from 'child_process';
import ora from 'ora';
import chalk from 'chalk';
import { TemplateRenderer } from './template';
export class ProjectGenerator {
constructor(config) {
this.config = config;
this.templateRenderer = new TemplateRenderer(config);
}
async generateProject(options) {
const spinner = ora('正在创建项目...').start();
try {
// 1. 检查项目目录
await this.checkProjectDirectory(options.name);
// 2. 创建项目目录
const projectPath = path.resolve(process.cwd(), options.name);
await fs.ensureDir(projectPath);
spinner.text = '正在复制模板文件...';
// 3. 复制模板文件
await this.templateRenderer.copyTemplate(options.template, projectPath, options);
// 4. 初始化Git仓库
if (options.useGit) {
spinner.text = '正在初始化Git仓库...';
await this.initGitRepository(projectPath);
}
// 5. 安装依赖
if (options.autoInstall) {
spinner.text = '正在安装依赖...';
await this.installDependencies(projectPath, options.packageManager);
}
// 6. 生成最终报告
await this.generateFinalReport(projectPath, options);
spinner.succeed(chalk.green(`项目 ${options.name} 创建成功!`));
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
spinner.fail(chalk.red(`项目创建失败: ${errorMessage}`));
throw error;
}
}
async checkProjectDirectory(projectName) {
const projectPath = path.resolve(process.cwd(), projectName);
if (await fs.pathExists(projectPath)) {
const files = await fs.readdir(projectPath);
if (files.length > 0) {
throw new Error(`目录 ${projectName} 已存在且不为空`);
}
}
// 检查目录名是否有效
if (!/^[a-zA-Z0-9-_]+$/.test(projectName)) {
throw new Error('项目名称只能包含字母、数字、连字符和下划线');
}
}
async initGitRepository(projectPath) {
try {
execSync('git init', { cwd: projectPath, stdio: 'pipe' });
execSync('git add .', { cwd: projectPath, stdio: 'pipe' });
execSync('git commit -m "Initial commit: Browser extension created with browser-plugin-creator"', {
cwd: projectPath,
stdio: 'pipe'
});
}
catch (error) {
console.warn(chalk.yellow('Git初始化失败,请手动初始化'));
}
}
async installDependencies(projectPath, packageManager) {
try {
const command = this.getInstallCommand(packageManager);
execSync(command, { cwd: projectPath, stdio: 'pipe' });
}
catch (error) {
console.warn(chalk.yellow(`依赖安装失败,请手动运行 ${packageManager} install`));
}
}
getInstallCommand(packageManager) {
switch (packageManager) {
case 'yarn':
return 'yarn install';
case 'pnpm':
return 'pnpm install';
default:
return 'npm install';
}
}
async generateFinalReport(projectPath, options) {
const report = {
name: options.name,
template: options.template,
path: projectPath,
features: options.features,
browserSupport: options.browserSupport,
nextSteps: [
`cd ${options.name}`,
options.autoInstall ? '' : `${options.packageManager} install`,
`${options.packageManager} run dev`,
'在浏览器中加载扩展进行测试'
].filter(Boolean)
};
await fs.writeJson(path.join(projectPath, '.browser-plugin-creator.json'), report, { spaces: 2 });
}
async validateTemplate(templateName) {
const config = this.config;
const templatesPath = config.templatesPath || path.join(__dirname, '../../templates');
const templatePath = path.join(templatesPath, templateName);
try {
const manifestPath = path.join(templatePath, 'manifest.json');
return await fs.pathExists(manifestPath);
}
catch {
return false;
}
}
async getTemplateInfo(templateName) {
const config = this.config;
const templatesPath = config.templatesPath || path.join(__dirname, '../../templates');
const templatePath = path.join(templatesPath, templateName);
try {
const manifestPath = path.join(templatePath, 'manifest.json');
const readmePath = path.join(templatePath, 'README.md');
const [manifest, readme] = await Promise.all([
fs.readJson(manifestPath),
await fs.pathExists(readmePath) ? fs.readFile(readmePath, 'utf8') : Promise.resolve('')
]);
return {
name: templateName,
displayName: manifest.name || templateName,
description: manifest.description || '',
version: manifest.version || '1.0.0',
permissions: manifest.permissions || [],
readme: readme
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
throw new Error(`无法读取模板信息: ${errorMessage}`);
}
}
}