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

Takes screenshots of your applications and inject them to the manifes… #20

Merged
merged 1 commit into from
Jan 10, 2024
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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Please have a look at the [Web app manifests](https://developer.mozilla.org/en-U

# Installation

Install the bundle with Composer: `composer require spomky-labs/phpwa`.
Install the bundle with Composer: `composer require --dev spomky-labs/phpwa`.

This project follows the [semantic versioning](http://semver.org/) strictly.

Expand Down
7 changes: 4 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"symfony/config": "^6.4|^7.0",
"symfony/dependency-injection": "^6.4|^7.0",
"symfony/filesystem": "^6.4|^7.0",
"symfony/finder": "^6.4",
"symfony/finder": "^6.4|^7.0",
"symfony/http-kernel": "^6.4|^7.0",
"symfony/mime": "^6.4|^7.0",
"symfony/routing": "^6.4|^7.0"
Expand All @@ -43,7 +43,7 @@
"phpstan/phpstan-phpunit": "^1.0",
"phpstan/phpstan-strict-rules": "^1.0",
"phpunit/phpunit": "^10.0",
"rector/rector": "^0.18",
"rector/rector": "^0.19",
"symfony/framework-bundle": "^6.4|^7.0",
"symfony/phpunit-bridge": "^6.4|^7.0",
"symplify/easy-coding-standard": "^12.0"
Expand All @@ -57,6 +57,7 @@
},
"suggest": {
"ext-gd": "Required to generate icons (or Imagick).",
"ext-imagick": "Required to generate icons (or GD)."
"ext-imagick": "Required to generate icons (or GD).",
"symfony/panther": "For generating screenshots directly from your application"
}
}
56 changes: 47 additions & 9 deletions src/Command/GenerateManifestCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace SpomkyLabs\PwaBundle\Command;

use Facebook\WebDriver\WebDriverDimension;
use JsonException;
use RuntimeException;
use SpomkyLabs\PwaBundle\ImageProcessor\ImageProcessor;
Expand All @@ -18,6 +19,7 @@
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpKernel\Config\FileLocator;
use Symfony\Component\Mime\MimeTypes;
use Symfony\Component\Panther\Client;
use Symfony\Component\Routing\RouterInterface;
use function count;
use function dirname;
Expand All @@ -33,16 +35,24 @@
{
private readonly MimeTypes $mime;

private readonly null|Client $webClient;

Check failure on line 38 in src/Command/GenerateManifestCommand.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 Test on ubuntu-latest

Property SpomkyLabs\PwaBundle\Command\GenerateManifestCommand::$webClient has unknown class Symfony\Component\Panther\Client as its type.

public function __construct(

Check failure on line 40 in src/Command/GenerateManifestCommand.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 Test on ubuntu-latest

Method SpomkyLabs\PwaBundle\Command\GenerateManifestCommand::__construct() has parameter $config with no value type specified in iterable type array.

Check failure on line 40 in src/Command/GenerateManifestCommand.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 Test on ubuntu-latest

Method SpomkyLabs\PwaBundle\Command\GenerateManifestCommand::__construct() has parameter $dest with no value type specified in iterable type array.
private readonly null|ImageProcessor $imageProcessor,
#[Autowire('@pwa.web_client')]

Check failure on line 42 in src/Command/GenerateManifestCommand.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 Test on ubuntu-latest

Parameter $webClient of method SpomkyLabs\PwaBundle\Command\GenerateManifestCommand::__construct() has invalid type Symfony\Component\Panther\Client.
null|Client $webClient,
#[Autowire('%spomky_labs_pwa.config%')]
private readonly array $config,
private readonly array $config,
#[Autowire('%spomky_labs_pwa.dest%')]
private readonly array $dest,
private readonly Filesystem $filesystem,
private readonly array $dest,
private readonly Filesystem $filesystem,
private readonly FileLocator $fileLocator,
private readonly ?RouterInterface $router = null,
private readonly ?RouterInterface $router = null,
) {
if ($webClient === null && class_exists(Client::class)) {
$webClient = Client::createChromeClient();
}
$this->webClient = $webClient;
$this->mime = MimeTypes::getDefault();
parent::__construct();
}
Expand Down Expand Up @@ -105,9 +115,9 @@
$hash = mb_substr(hash('sha256', $data), 0, 8);
file_put_contents($tempFilename, $data);
$mime = $this->mime->guessMimeType($tempFilename);
$extension = $this->mime->getExtensions($mime);

Check failure on line 118 in src/Command/GenerateManifestCommand.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 Test on ubuntu-latest

Parameter #1 $mimeType of method Symfony\Component\Mime\MimeTypes::getExtensions() expects string, string|null given.

if (empty($extension)) {

Check failure on line 120 in src/Command/GenerateManifestCommand.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 Test on ubuntu-latest

Construct empty() is not allowed. Use more strict comparison.
throw new RuntimeException(sprintf('Unable to guess the extension for the mime type "%s"', $mime));
}

Expand All @@ -118,7 +128,7 @@
file_put_contents($localFilename, $data);
$this->filesystem->remove($tempFilename);

return [

Check failure on line 131 in src/Command/GenerateManifestCommand.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 Test on ubuntu-latest

Method SpomkyLabs\PwaBundle\Command\GenerateManifestCommand::storeFile() should return array{src: string, type: string} but returns array{src: non-falsy-string, type: string|null}.
'src' => sprintf('%s/%s', $prefixUrl, $filename),
'type' => $mime,
];
Expand All @@ -130,10 +140,10 @@
private function storeScreenshot(string $data, ?string $format, ?string $formFactor): array
{
if ($format !== null) {
$data = $this->imageProcessor->process($data, null, null, $format);

Check failure on line 143 in src/Command/GenerateManifestCommand.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 Test on ubuntu-latest

Cannot call method process() on SpomkyLabs\PwaBundle\ImageProcessor\ImageProcessor|null.
}

['width' => $width, 'height' => $height] = $this->imageProcessor->getSizes($data);

Check failure on line 146 in src/Command/GenerateManifestCommand.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 Test on ubuntu-latest

Cannot call method getSizes() on SpomkyLabs\PwaBundle\ImageProcessor\ImageProcessor|null.
$size = sprintf('%sx%s', $width, $height);

$fileData = $this->storeFile(
Expand All @@ -148,7 +158,7 @@
];
}

return $fileData + [

Check failure on line 161 in src/Command/GenerateManifestCommand.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 Test on ubuntu-latest

Method SpomkyLabs\PwaBundle\Command\GenerateManifestCommand::storeScreenshot() should return array{src: string, type: string, sizes: string, form_factor: string|null} but returns array{sizes: non-falsy-string, form_factor?: string, src: string, type: string}.
'sizes' => $size,
];
}
Expand Down Expand Up @@ -242,13 +252,36 @@
$manifest['screenshots'] = [];
$config = [];
foreach ($this->config['screenshots'] as $screenshot) {
$src = $screenshot['src'];
if (! $this->filesystem->exists($src)) {
continue;
if (isset($screenshot['src'])) {
$src = $screenshot['src'];
if (! $this->filesystem->exists($src)) {
continue;
}
foreach ($this->findImages($src) as $image) {
$data = $screenshot;
$data['src'] = $image;
$config[] = $data;
}
}
foreach ($this->findImages($src) as $image) {
if (isset($screenshot['path'])) {
$path = $screenshot['path'];
$height = $screenshot['height'];
$width = $screenshot['width'];
unset($screenshot['path'], $screenshot['height'], $screenshot['width']);

$client = clone $this->webClient;
$client->request('GET', $path);
$tmpName = $this->filesystem->tempnam('', 'pwa-');
$client->manage()
->window()
->setSize(new WebDriverDimension($width, $height));
$client->manage()
->window()
->fullscreen();
$client->takeScreenshot($tmpName);
$data = $screenshot;
$data['src'] = $image;
$data['src'] = $tmpName;
$data['delete'] = true;
$config[] = $data;
}
}
Expand All @@ -259,6 +292,8 @@
$io->error(sprintf('Unable to read the icon "%s"', $screenshot['src']));
return self::FAILURE;
}
$delete = $screenshot['delete'] ?? false;
unset($screenshot['delete']);
$screenshotManifest = $this->storeScreenshot(
$data,
$screenshot['format'] ?? null,
Expand All @@ -271,6 +306,9 @@
$screenshotManifest['platform'] = $screenshot['platform'];
}
$manifest['screenshots'][] = $screenshotManifest;
if ($delete) {
$this->filesystem->remove($screenshot['src']);
}
}

return $manifest;
Expand Down
109 changes: 77 additions & 32 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,38 +77,8 @@ private function setupShortcuts(ArrayNodeDefinition $node): void
private function setupScreenshots(ArrayNodeDefinition $node): void
{
$node->children()
->arrayNode('screenshots')
->treatFalseLike([])
->treatTrueLike([])
->treatNullLike([])
->info('The screenshots of the application.')
->arrayPrototype()
->children()
->scalarNode('src')
->isRequired()
->info('The path to the screenshot.')
->example('screenshot/lowres.webp')
->end()
->scalarNode('form_factor')
->info('The form factor of the screenshot. Will guess the form factor if not set.')
->example(['wide', 'narrow'])
->end()
->scalarNode('label')
->info('The label of the screenshot.')
->example('Homescreen of Awesome App')
->end()
->scalarNode('platform')
->info('The platform of the screenshot.')
->example(
['android', 'windows', 'chromeos', 'ipados', 'ios', 'kaios', 'macos', 'windows', 'xbox']
)
->end()
->scalarNode('format')
->info('The format of the screenshot. Will convert the file if set.')
->example(['jpg', 'png', 'webp'])
->end()
->end()
->end()
->append($this->getScreenshotsNode())
->end()
;
}

Expand Down Expand Up @@ -293,6 +263,10 @@ private function setupSimpleOptions(ArrayNodeDefinition $node): void
->info('The image processor to use to generate the icons of different sizes.')
->example(GDImageProcessor::class)
->end()
->scalarNode('web_client')
->defaultNull()
->info('The Panther Client for generating screenshots. If not set, the default client will be used.')
->end()
->scalarNode('icon_folder')
->defaultValue('%kernel.project_dir%/public/pwa')
->info('The folder where the icons will be generated.')
Expand Down Expand Up @@ -468,4 +442,75 @@ private function setupServiceWorker(ArrayNodeDefinition $node): void
->end()
->end();
}

private function getScreenshotsNode(): ArrayNodeDefinition
{
$treeBuilder = new TreeBuilder('screenshots');
$node = $treeBuilder->getRootNode();
assert($node instanceof ArrayNodeDefinition);
$node
->treatFalseLike([])
->treatTrueLike([])
->treatNullLike([])
->arrayPrototype()
->validate()
->ifTrue(static fn (array $v): bool => ! (isset($v['src']) xor isset($v['path'])))
->thenInvalid('Either "src", "route" or "path" must be set.')
->end()
->validate()
->ifTrue(static function (array $v): bool {
if (isset($v['src'])) {
return false;
}

if (! isset($v['height']) || ! isset($v['width'])) {
return true;
}

return false;
})
->thenInvalid('When using "path", "height" and "width" must be set.')
->end()
->children()
->scalarNode('src')
->info('The path to the screenshot.')
->example('screenshot/lowres.webp')
->end()
->scalarNode('path')
->info('The path to an application page. The screenshot will be generated.')
->example('https://example.com')
->end()
->scalarNode('height')
->defaultNull()
->info('When using "route" or "path", the height of the screenshot.')
->example('1080')
->end()
->scalarNode('width')
->defaultNull()
->info('When using "route" or "path", the height of the screenshot.')
->example('1080')
->end()
->scalarNode('form_factor')
->info('The form factor of the screenshot. Will guess the form factor if not set.')
->example(['wide', 'narrow'])
->end()
->scalarNode('label')
->info('The label of the screenshot.')
->example('Homescreen of Awesome App')
->end()
->scalarNode('platform')
->info('The platform of the screenshot.')
->example(
['android', 'windows', 'chromeos', 'ipados', 'ios', 'kaios', 'macos', 'windows', 'xbox']
)
->end()
->scalarNode('format')
->info('The format of the screenshot. Will convert the file if set.')
->example(['jpg', 'png', 'webp'])
->end()
->end()
->end();

return $node;
}
}
5 changes: 4 additions & 1 deletion src/DependencyInjection/SpomkyLabsPwaExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ public function load(array $configs, ContainerBuilder $container): void
if ($config['image_processor'] !== null) {
$container->setAlias(ImageProcessor::class, $config['image_processor']);
}
unset($config['image_processor']);
if ($config['web_client'] !== null) {
$container->setAlias('pwa.web_client', $config['web_client']);
}
unset($config['image_processor'], $config['web_client']);
$params = [
'icon_folder',
'icon_prefix_url',
Expand Down
2 changes: 0 additions & 2 deletions src/Resources/config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
declare(strict_types=1);

use SpomkyLabs\PwaBundle\Command\GenerateManifestCommand;
use SpomkyLabs\PwaBundle\Command\WorkboxInitCommand;
use SpomkyLabs\PwaBundle\ImageProcessor\GDImageProcessor;
use SpomkyLabs\PwaBundle\ImageProcessor\ImagickImageProcessor;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
Expand All @@ -17,7 +16,6 @@
;

$container->set(GenerateManifestCommand::class);
$container->set(WorkboxInitCommand::class);

if (extension_loaded('imagick')) {
$container
Expand Down