ソースを参照

feat: 完成composer.json创建和目录结构创建

runphp 6 ヶ月 前
コミット
917a43b534

+ 23 - 1
bin/sixshop-maker

@@ -1,4 +1,26 @@
 #!/usr/bin/env php
 #!/usr/bin/env php
 <?php
 <?php
 
 
-require __DIR__ . '/../vendor/autoload.php';
+$dir = __DIR__.'/..';
+
+if (!file_exists($dir.'/autoload.php')) {
+    $dir = __DIR__.'/../vendor';
+}
+
+if (!file_exists($dir.'/autoload.php')) {
+    $dir = __DIR__.'/../../..';
+}
+
+if (!file_exists($dir.'/autoload.php')) {
+    echo 'Autoload not found.';
+    exit(1);
+}
+
+require $dir.'/autoload.php';
+
+use SixShop\MakerBundle\Maker;
+use Symfony\Component\Console\Application;
+
+$application = new Application();
+$application->add(new Maker());
+$application->run();

+ 171 - 0
src/Generator/ComposerGenerator.php

@@ -0,0 +1,171 @@
+<?php
+declare(strict_types=1);
+
+namespace SixShop\MakerBundle\Generator;
+
+use Symfony\Component\Console\Style\SymfonyStyle;
+
+class ComposerGenerator
+{
+    private string $templatePath;
+
+    public function __construct()
+    {
+        $this->templatePath = __DIR__ . '/../../templates/composer.json.tpl.php';
+    }
+
+    /**
+     * 获取用户输入的包信息
+     */
+    public function gatherPackageInfo(SymfonyStyle $io): ?array
+    {
+        try {
+            // 获取包名
+            $packageName = $io->ask('请输入Composer包名 (例如: six-shop/hello 或sixdec/abc)');
+            
+            if (empty($packageName)) {
+                throw new \InvalidArgumentException('包名不能为空!');
+            }
+            
+            // 验证包名格式
+            if (!preg_match('/^[a-z0-9-_]+\/[a-z0-9-_]+$/', $packageName)) {
+                throw new \InvalidArgumentException('包名格式不正确! 正确格式: six-shop/hello 或sixdec/abc');
+            }
+            
+            // 获取包描述
+            $description = $io->ask('请输入包描述 (可选)', 'A SixShop extension package');
+            
+            // 获取命名空间
+            $defaultNamespace = $this->generateDefaultNamespace($packageName);
+            $namespace = $io->ask('请输入命名空间 (例如: SixShop\\\\Hello)', $defaultNamespace);
+            
+            // 获取扩展ID
+            $defaultId = str_replace('/', '-', $packageName);
+            $id = $io->ask('请输入扩展ID (例如: six-shop-hello)', $defaultId);
+            
+            return [
+                'packageName' => $packageName,
+                'description' => $description,
+                'namespace' => $namespace,
+                'id' => $id
+            ];
+        } catch (\InvalidArgumentException $e) {
+            $io->error($e->getMessage());
+            return null;
+        }
+    }
+    
+    /**
+     * 生成默认命名空间
+     */
+    private function generateDefaultNamespace(string $packageName): string
+    {
+        $parts = explode('/', $packageName);
+        $vendor = $this->convertToNamespace($parts[0]);
+        $package = $this->convertToNamespace($parts[1]);
+        return $vendor . '\\\\' . $package;
+    }
+    
+    /**
+     * 将字符串转换为命名空间格式
+     */
+    private function convertToNamespace(string $string): string
+    {
+        $parts = explode('-', $string);
+        $parts = array_map('ucfirst', $parts);
+        return implode('', $parts);
+    }
+
+    /**
+     * 生成composer.json内容
+     */
+    public function generateContent(string $packageName, string $namespace, string $id, string $description = 'A SixShop extension package'): string
+    {
+        if (!file_exists($this->templatePath)) {
+            throw new \RuntimeException('模板文件不存在: ' . $this->templatePath);
+        }
+
+        // 准备模板变量
+        $name = $packageName;
+        
+        // 生成composer.json内容
+        ob_start();
+        include $this->templatePath;
+        return ob_get_clean();
+    }
+
+    /**
+     * 保存composer.json文件到指定目录
+     */
+    public function saveComposerFile(string $packageName, string $content, SymfonyStyle $io): bool
+    {
+        // 获取vendor上级目录并构建路径: runtime/extension/{package-name}/
+        $vendorDir = dirname(__DIR__, 5); // 从当前文件位置向上5级到vendor上级目录
+        $extensionDir = $vendorDir . '/runtime/extension/' . $packageName;
+
+        // 确保目录存在
+        if (!is_dir($extensionDir)) {
+            if (!mkdir($extensionDir, 0755, true)) {
+                $io->error('无法创建目录: ' . $extensionDir);
+                return false;
+            }
+        }
+
+        $outputPath = $extensionDir . '/composer.json';
+        if (file_put_contents($outputPath, $content) !== false) {
+            $io->success('composer.json 文件已成功生成: ' . $outputPath);
+            return true;
+        } else {
+            $io->error('无法写入文件: ' . $outputPath);
+            return false;
+        }
+    }
+
+    /**
+     * 完整的生成和保存流程
+     */
+    public function createComposerJson(string $packageName, string $namespace, string $id, string $description, SymfonyStyle $io): bool
+    {
+        try {
+            // 生成内容
+            $content = $this->generateContent($packageName, $namespace, $id, $description);
+
+            // 输出生成的内容
+            $io->section('生成的 composer.json 内容:');
+            $io->text($content);
+
+            // 询问是否保存文件
+            if ($io->confirm('是否将内容保存到 composer.json 文件?', true)) {
+                return $this->saveComposerFile($packageName, $content, $io);
+            }
+
+            return true;
+        } catch (\Exception $e) {
+            $io->error('生成 composer.json 时发生错误: ' . $e->getMessage());
+            return false;
+        }
+    }
+    
+    /**
+     * 完整的从用户输入到生成文件的工作流
+     */
+    public function createComposerFromUserInput(SymfonyStyle $io): bool
+    {
+        try {
+            // 获取用户输入
+            $packageInfo = $this->gatherPackageInfo($io);
+            
+            // 生成composer.json
+            return $this->createComposerJson(
+                $packageInfo['packageName'],
+                $packageInfo['namespace'],
+                $packageInfo['id'],
+                $packageInfo['description'],
+                $io
+            );
+        } catch (\InvalidArgumentException $e) {
+            $io->error($e->getMessage());
+            return false;
+        }
+    }
+}

+ 129 - 0
src/Generator/DirectoryGenerator.php

@@ -0,0 +1,129 @@
+<?php
+declare(strict_types=1);
+
+namespace SixShop\MakerBundle\Generator;
+
+use Symfony\Component\Console\Style\SymfonyStyle;
+
+class DirectoryGenerator
+{
+    /**
+     * 生成扩展的基础目录结构
+     */
+    public function createDirectoryStructure(string $packageName, SymfonyStyle $io): bool
+    {
+        try {
+            // 获取基础路径
+            $basePath = $this->getBasePath($packageName);
+            
+            // 定义需要创建的目录结构
+            $directories = $this->getDirectoryStructure();
+            
+            $io->section('创建目录结构:');
+            
+            // 创建所有目录
+            foreach ($directories as $dir) {
+                $fullPath = $basePath . '/' . $dir;
+                if (!$this->createDirectory($fullPath, $io)) {
+                    return false;
+                }
+            }
+            
+            $io->success('目录结构创建成功!');
+            return true;
+            
+        } catch (\Exception $e) {
+            $io->error('创建目录结构时发生错误: ' . $e->getMessage());
+            return false;
+        }
+    }
+    
+    /**
+     * 获取扩展基础路径
+     */
+    private function getBasePath(string $packageName): string
+    {
+        // 获取vendor上级目录并构建路径: runtime/extension/{package-name}/
+        $vendorDir = dirname(__DIR__, 5); // 从当前文件位置向上5级到vendor上级目录
+        return $vendorDir . '/runtime/extension/' . $packageName;
+    }
+    
+    /**
+     * 定义扩展的目录结构(参考 six-shop/eav)
+     */
+    private function getDirectoryStructure(): array
+    {
+        return [
+            'database',
+            'database/migrations',
+            'route',
+            'src',
+            'src/Controller',
+            'src/Entity',
+            'src/Enum',
+            'src/Event',
+            'src/Hook',
+            'src/Job',
+            'src/Middleware',
+            'src/Model',
+            'src/Service',
+            'src/Trait',
+            'src/Validator',
+            'test',
+            'docs'
+        ];
+    }
+    
+    /**
+     * 创建单个目录
+     */
+    private function createDirectory(string $path, SymfonyStyle $io): bool
+    {
+        if (is_dir($path)) {
+            $io->text("目录已存在: $path");
+            return true;
+        }
+        
+        if (mkdir($path, 0755, true)) {
+            $io->text("✓ 创建目录: $path");
+            return true;
+        } else {
+            $io->error("✗ 无法创建目录: $path");
+            return false;
+        }
+    }
+    
+    /**
+     * 检查目录是否已存在
+     */
+    public function directoryExists(string $packageName): bool
+    {
+        $basePath = $this->getBasePath($packageName);
+        return is_dir($basePath);
+    }
+    
+    /**
+     * 获取目录结构信息
+     */
+    public function getDirectoryInfo(string $packageName): array
+    {
+        $basePath = $this->getBasePath($packageName);
+        $directories = $this->getDirectoryStructure();
+        
+        $info = [
+            'basePath' => $basePath,
+            'directories' => [],
+            'exists' => $this->directoryExists($packageName)
+        ];
+        
+        foreach ($directories as $dir) {
+            $fullPath = $basePath . '/' . $dir;
+            $info['directories'][$dir] = [
+                'path' => $fullPath,
+                'exists' => is_dir($fullPath)
+            ];
+        }
+        
+        return $info;
+    }
+}

+ 58 - 1
src/Maker.php

@@ -2,7 +2,64 @@
 declare(strict_types=1);
 declare(strict_types=1);
 namespace SixShop\MakerBundle;
 namespace SixShop\MakerBundle;
 
 
-class Maker
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Style\SymfonyStyle;
+use SixShop\MakerBundle\Generator\ComposerGenerator;
+use SixShop\MakerBundle\Generator\DirectoryGenerator;
+
+class Maker extends Command
 {
 {
+    protected static $defaultName = 'create_extension';
+    protected static $defaultDescription = '创建SixShop扩展';
+    
+    private ComposerGenerator $composerGenerator;
+    private DirectoryGenerator $directoryGenerator;
+    
+    public function __construct()
+    {
+        parent::__construct();
+        $this->composerGenerator = new ComposerGenerator();
+        $this->directoryGenerator = new DirectoryGenerator();
+    }
 
 
+    protected function configure(): void
+    {
+        $this->setName('create_extension')
+             ->setDescription(self::$defaultDescription);
+    }
+    protected function execute(InputInterface $input, OutputInterface $output): int
+    {
+        $io = new SymfonyStyle($input, $output);
+        
+        // 获取用户输入的包信息
+        $packageInfo = $this->composerGenerator->gatherPackageInfo($io);
+        if (!$packageInfo) {
+            return Command::FAILURE;
+        }
+        
+        // 生成 composer.json 文件
+        $io->section('生成 composer.json 文件');
+        if (!$this->composerGenerator->createComposerJson(
+            $packageInfo['packageName'],
+            $packageInfo['namespace'],
+            $packageInfo['id'],
+            $packageInfo['description'],
+            $io
+        )) {
+            return Command::FAILURE;
+        }
+        
+        // 创建目录结构
+        $io->section('创建目录结构');
+        if (!$this->directoryGenerator->createDirectoryStructure($packageInfo['packageName'], $io)) {
+            return Command::FAILURE;
+        }
+        
+        $io->success('扩展创建完成!');
+        return Command::SUCCESS;
+    }
 }
 }

+ 33 - 0
templates/composer.json.tpl.php

@@ -0,0 +1,33 @@
+{
+  "name": "<?= $name ?>",
+  "description": "<?= $description ?>",
+  "type": "sixshop-extension",
+  "keywords": [
+    "sixshop",
+    "thinkphp"
+  ],
+  "require": {
+    "php": ">=8.3",
+    "six-shop/core": ">=0.6 <1.0"
+  },
+  "authors": [
+    {
+      "name": "hui he",
+      "email": "runphp@qq.com"
+    }
+  ],
+  "license": "MIT",
+  "autoload": {
+    "psr-4": {
+      "<?= $namespace ?>": "src"
+    }
+  },
+  "extra": {
+    "sixshop": {
+      "id": "<?= $id ?>",
+      "class": "<?= $namespace ?>\\Extension"
+    }
+  },
+  "minimum-stability": "dev",
+  "prefer-stable": true
+}