Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Import xulieta-plugin-installer into the project #90

Merged
merged 8 commits into from
Feb 23, 2022
Merged
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: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:

strategy:
matrix:
php: ['7.4', '8.0']
php: ['7.4', '8.0', '8.1']
dependencies: ['normal', 'authoritative']

steps:
Expand Down
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,26 @@ with the following configuration format:
- `outputFormatters`: personalized output formatter
- `exclude`: excluded directory or files

## Plugins

`Xulieta` will automatically scan dependencies to see if there is
any package that is providing default configurations.

If you want your plugin to take advantage of that functionality,
we expect you to provide some information on your `composer.json`
file, ie:

```json
{
"extra": {
"xulieta": {
"parser": ["Malukenho\\QuoPrimumTempore\\JsonParser"],
"validator": ["Malukenho\\QuoPrimumTempore\\JsonValidator"]
}
}
}
```

## Contributors ✨

Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
Expand Down
38 changes: 24 additions & 14 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "codelicia/xulieta",
"description": "Xulieta is a light php binary that lint documentation snippets",
"type": "library",
"type": "composer-plugin",
"license": "MIT",
"homepage": "https://github.com/codelicia/xulieta",
"keywords": [
Expand All @@ -25,31 +25,41 @@
}
},
"config": {
"sort-packages": true
"sort-packages": true,
"allow-plugins": {
"infection/extension-installer": true,
"malukenho/mcbumpface": true,
"dealerdirect/phpcodesniffer-composer-installer": true
}
},
"require": {
"php": "~7.4 || ~8.0",
"beberlei/assert": "^v3.2.7",
"beberlei/assert": "^v3.3.2",
"composer-plugin-api": "^2.2",
"doctrine/rst-parser": "^0.1.0 || ^0.3.0",
"nikic/php-parser": "^v4.10.2",
"nikic/php-parser": "^v4.13.2",
"ocramius/package-versions": "^1.9",
"symfony/config": "^v5.1.8",
"symfony/console": "^v5.1.8",
"symfony/finder": "^v5.1.8",
"symfony/process": "^v5.1.8",
"webmozart/assert": "^1.9.1"
"symfony/config": "^v5.4.3",
"symfony/console": "^v5.4.3",
"symfony/finder": "^v5.4.3",
"symfony/process": "^v5.4.3",
"webmozart/assert": "^1.10.0"
},
"require-dev": {
"composer/composer": "^2.2.6",
"doctrine/coding-standard": "^8.2.0 || ^9.0.0",
"infection/infection": "^0.19.2",
"infection/infection": "^0.26.5",
"malukenho/mcbumpface": "^1.1.5",
"phpunit/phpunit": "^9.4.3",
"phpunit/phpunit": "^9.5.14",
"roave/security-advisories": "dev-master",
"staabm/annotate-pull-request-from-checkstyle": "^1.4.0",
"vimeo/psalm": "^4.1.1"
"staabm/annotate-pull-request-from-checkstyle": "^1.8.2",
"vimeo/psalm": "^4.21.0"
},
"extra": {
"class": "Codelicia\\Xulieta\\AutoPlugin\\Register"
},
"scripts": {
"changelog": "git log $(git describe --abbrev=0 --tags)...HEAD --no-merges --pretty=format:\"* [%h](http://github.com/codelicia/xulieta/commit/%H) %s (%cN)\""
"post-package-install": "Codelicia\\Xulieta\\AutoPlugin\\Register::scan"
},
"bin": [
"bin/xulieta"
Expand Down
6 changes: 6 additions & 0 deletions default-config.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<xulieta xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="./vendor/codelicia/xulieta/xulieta.xsd">
<exclude>vendor</exclude>
<exclude>node_modules</exclude>
</xulieta>
180 changes: 180 additions & 0 deletions src/AutoPlugin/Register.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
<?php

declare(strict_types=1);

namespace Codelicia\Xulieta\AutoPlugin;

use Composer\Composer;
use Composer\DependencyResolver\Operation\InstallOperation;
use Composer\EventDispatcher\EventSubscriberInterface;
use Composer\Installer\PackageEvent;
use Composer\Installer\PackageEvents;
use Composer\IO\IOInterface;
use Composer\Plugin\PluginInterface;
use DOMDocument;
use DOMElement;
use DOMException;
use Symfony\Component\Config\Util\XmlUtils;

use function assert;
use function dirname;
use function file_exists;
use function in_array;
use function is_array;

/**
* Based on https://github.com/laminas/laminas-component-installer/blob/2.5.x/src/ComponentInstaller.php
*
* In order to have your xulieta extension auto configurable, you need to put in
* your composer.json the following keys, if applicable:
*
* - extra.xulieta.parser
* - extra.xulieta.validator
*
* The values should have the FQCN as the following example:
*
* <code class="lang-javascript">
* {
* "extra": {
* "xulieta": {
* "parser": ["Malukenho\\QuoPrimumTempore\\JsonParser"],
* "validator": ["Malukenho\\QuoPrimumTempore\\JsonValidator"]
* }
* }
* }
* </code>
*
* @internal
*/
final class Register implements PluginInterface, EventSubscriberInterface
{
public static function scan(PackageEvent $event): void
{
if (! $event->isDevMode()) {
return;
}

$operation = $event->getOperation();
assert($operation instanceof InstallOperation);
$package = $operation->getPackage();

/** @var array<string,mixed> $packageExtra */
$packageExtra = $package->getExtra();
$extra = self::getExtraMetadata($packageExtra);

if (empty($extra)) {
// Package does not define anything of interest; do nothing.
return;
}

self::injectModuleIntoConfig($extra, $event->getIO(), $event->getComposer());
}

/**
* Retrieve the metadata from the "extra" section
*
* @param array{xulieta?: object|array{parser?: string, validator?: string}} $extra
*
* @return array<string,mixed>
*/
private static function getExtraMetadata(array $extra): array
{
$pluginConfiguration = [];

if (isset($extra['xulieta']) && is_array($extra['xulieta'])) {
/** @var array<string,mixed> $pluginConfiguration */
$pluginConfiguration = $extra['xulieta'];
}

return $pluginConfiguration;
}

private static function injectModuleIntoConfig(array $extra, IOInterface $io, Composer $composer): void
{
$rootDir = dirname($composer->getConfig()->getConfigSource()->getName());
$xulietaConfigFile = $readFile = $rootDir . '/.xulieta.xml';

if (! file_exists($xulietaConfigFile)) {
if (! $io->askConfirmation('Do you want us to create a ".xulieta.xml" for you? ')) {
return;
}

$readFile = __DIR__ . '/../../default-config.xml.dist';
}

$xml = XmlUtils::loadFile($readFile);

self::appendChild($xml, $extra, 'parser');
self::appendChild($xml, $extra, 'validator');

// @fixme: workaround to save properly formatted xml
$domxml = new DOMDocument('1.0');
$domxml->preserveWhiteSpace = false;
$domxml->formatOutput = true;
$domxml->loadXML($xml->saveXML());
$domxml->save($xulietaConfigFile);

$io->write('Xulieta configuration is up-to-date...');
}

/** @throws DOMException */
private static function appendChild(DOMDocument $document, array $extra, string $tag): void
{
/** @var DOMElement $root */
$root = $document->documentElement;

$validators = $root->getElementsByTagName($tag);
$b = [];

foreach ($validators->getIterator() as $taggedElements) {
assert($taggedElements instanceof DOMElement);
$b[] = $taggedElements->textContent;
}

if (! isset($extra[$tag])) {
return;
}

/** @var string $toBeRegistered */
foreach ($extra[$tag] as $toBeRegistered) {
if (in_array($toBeRegistered, $b, true)) {
continue;
}

if (! isset($taggedElements)) {
$root->append($document->createElement($tag, $toBeRegistered));

continue;
}

$taggedElements?->parentNode?->insertBefore(
$document->createElement($tag, $toBeRegistered),
$taggedElements
);
}
}

/** @psalm-return array{post-package-install: string} */
public static function getSubscribedEvents(): array
{
return [PackageEvents::POST_PACKAGE_INSTALL => 'scan'];
}

/** @return void */
public function deactivate(Composer $composer, IOInterface $io)
{
// Intentionally left blank
}

/** @return void */
public function uninstall(Composer $composer, IOInterface $io)
{
// Intentionally left blank
}

/** @return void */
public function activate(Composer $composer, IOInterface $io)
{
// Intentionally left blank
}
}
2 changes: 1 addition & 1 deletion tests/functional/md-syntax-error-checkstyle.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ $checkRunner('tests/assets/syntax-error-checkstyle.md --output=checkstyle');
<?xml version="1.0" encoding="UTF-8"?>
<checkstyle>
<file name="tests/assets/syntax-error-checkstyle.md">
<error line="7" column="1" severity="error" message="Codelicia/Xulieta: Syntax error, unexpected '}', expecting ';' on line 5" source="Codelicia/Xulieta"/>
<error line="7" column="1" severity="error" message="Codelicia/Xulieta: Syntax error, unexpected &#039;}&#039;, expecting &#039;;&#039; on line 5" source="Codelicia/Xulieta"/>
</file>
</checkstyle>