diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dde77db..46e6038 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -78,7 +78,7 @@ This is also important for the Utopia-php lead developers to be able to give tec ## Adding A New Adapter -You can follow our [Adding new Detector Adapter](docs/add-new-detector-adapter.md) tutorial to add a new Detector adapter. +You can follow our [Adding new Detector Adapter](docs/add-new-adapter.md) tutorial to add a new Detector adapter. ## Other Ways to Help diff --git a/docs/add-new-adapter.md b/docs/add-new-adapter.md new file mode 100644 index 0000000..3094299 --- /dev/null +++ b/docs/add-new-adapter.md @@ -0,0 +1,215 @@ +# Add new Detector and Detection Adapters + +To get started with implementing a new detector and detection adapters, start by reviewing the [README](/README.md) to understand the goals of this library. + +### Introduction ๐Ÿ“ +- A `Detector` is a class that analyzes input files to identify specific characteristics of a project. +- A `Detection` class represents a specific type of detection (like Framework, Runtime, Packager, etc.) and contains the logic for identifying that type. +- To add a new detector, you need to extend the `Detector` parent class and define the required methods. +- To add a new detection class, you need to extend the appropriate `Detection` parent class (Framework, Runtime, Packager, or Rendering). + +### File Structure ๐Ÿ“‚ + +Below are outlined the most useful files for adding a new detector and detection class: + +```bash +. +โ”œโ”€โ”€ src # Source code +โ”‚ โ”œโ”€โ”€ Detector.php # Parent class for all detectors +โ”‚ โ”œโ”€โ”€ Detection.php # Parent class for all detections +โ”‚ โ”œโ”€โ”€ Detector/ # Where your new detector goes! +โ”‚ โ”‚ โ”œโ”€โ”€ Framework.php # Framework detector +โ”‚ โ”‚ โ”œโ”€โ”€ Packager.php # Packager detector +โ”‚ โ”‚ โ”œโ”€โ”€ Runtime.php # Runtime detector +โ”‚ โ”‚ โ””โ”€โ”€ Rendering.php # Rendering detector +โ”‚ โ””โ”€โ”€ Detection/ # Where your new detection class goes! +โ”‚ โ”œโ”€โ”€ Framework/ # Framework detections +โ”‚ โ”œโ”€โ”€ Packager/ # Packager detections +โ”‚ โ”œโ”€โ”€ Runtime/ # Runtime detections +โ”‚ โ””โ”€โ”€ Rendering/ # Rendering detections +โ””โ”€โ”€ tests + โ””โ”€โ”€ unit/ # Where tests for your new detector/detection go! +``` + +### Extend the Detector ๐Ÿ’ป + +Create your new detector file in `src/Detector` and extend the parent class: + +```php + + */ + protected array $options = []; + + /** + * @param array $inputs + */ + public function __construct(protected array $inputs) + { + } + + public function detect(): ?YourDetection + { + foreach ($this->options as $detector) { + $detectorFiles = $detector->getFiles(); + $matches = array_intersect($detectorFiles, $this->inputs); + + if (count($matches) > 0) { + return $detector; + } + } + + return null; + } +} +``` + +### Extend the Detection Class ๐Ÿ’ป + +Create your new detection class in the appropriate directory under `src/Detection` and extend the parent class: + +```php + + */ + public function getFiles(): array + { + return ['file1.ext', 'file2.ext']; + } + + // Implement other required methods based on parent class +} +``` + +### Testing ๐Ÿงช + +Add tests for your new detector and detection class in `tests/unit/DetectorTest.php`. Here's an example: + +```php +public function testYourDetector(array $files, ?string $expectedName): void +{ + $detector = new YourDetector($files); + $detector->addOption(new YourDetection()); + + $detected = $detector->detect(); + + if ($expectedName) { + $this->assertNotNull($detected); + $this->assertEquals($expectedName, $detected->getName()); + } else { + $this->assertNull($detected); + } +} + +/** + * @return array, string|null}> + */ +public function yourDetectorDataProvider(): array +{ + return [ + [['file1.ext', 'file2.ext'], 'your-detection-name'], + [['other.ext'], null], + ]; +} +``` + +### Tips and Tricks ๐Ÿ’ก + +1. **Choose the Right Parent Class** + - For framework detection: Extend `Detection\Framework` + - For runtime detection: Extend `Detection\Runtime` + - For packager detection: Extend `Detection\Packager` + - For rendering detection: Extend `Detection\Rendering` + +2. **Implement Required Methods** + - Each detection type has specific required methods + - Framework detections need: `getName()`, `getFiles()`, `getInstallCommand()`, `getBuildCommand()`, `getOutputDirectory()` + - Runtime detections need: `getName()`, `getLanguages()`, `getFileExtensions()`, `getFiles()`, `getCommands()`, `getEntrypoint()` + - Packager detections need: `getName()`, `getFiles()` + - Rendering detections need: `getName()` + +3. **File Detection** + - Use specific file patterns that uniquely identify your detection + - Consider both common and edge cases + - Include configuration files, lock files, and other distinctive files + +4. **Testing** + - Test both positive and negative cases + - Include edge cases in your test data + - Follow the existing test patterns in the codebase + +5. **Performance** + - Keep detection logic simple and efficient + - Minimize file system operations + - Use array operations for file matching + +### Example Implementation + +Here's a complete example of implementing a new framework detection: + +```php + + */ + public function getFiles(): array + { + return ['my-framework.config.js', 'my-framework.lock']; + } + + public function getInstallCommand(): string + { + return 'npm install'; + } + + public function getBuildCommand(): string + { + return 'npm run build'; + } + + public function getOutputDirectory(): string + { + return './dist'; + } +} +``` + +Only include dependencies strictly necessary for the adapter, preferably official PHP libraries, if available. + +### Testing with Docker ๐Ÿ› ๏ธ + +The existing test suite is helpful when developing a new Detector/ Detection adapter. Use official Docker images from trusted sources. Add new tests for your new Detector/ Detection adapter in `tests/unit/DetectorTest.php` test class. The specific `docker-compose` command for testing can be found in the [README](/README.md#tests). \ No newline at end of file