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

RFC: Vytažení do nette extension #1

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 21 additions & 0 deletions Vite/src/AssetFilter.php
@@ -0,0 +1,21 @@
<?php declare(strict_types=1);

namespace MMEE\Vite;

final class AssetFilter
{
private Service $vite;


public function __construct(Service $vite)
{
$this->vite = $vite;
}


public function __invoke(string $path): string
{
return $this->vite->getAsset($path);
}

}
8 changes: 8 additions & 0 deletions Vite/src/ManifestFileDoesNotExistsException.php
@@ -0,0 +1,8 @@
<?php declare(strict_types=1);

namespace MMEE\Vite;

final class ManifestFileDoesNotExistsException extends \RuntimeException
{

}
158 changes: 158 additions & 0 deletions Vite/src/Nette/Extension.php
@@ -0,0 +1,158 @@
<?php declare(strict_types=1);

namespace MMEE\Vite\Nette;

use MMEE\Vite\AssetFilter;
use MMEE\Vite\ManifestFileDoesNotExistsException;
use MMEE\Vite\Service;
use MMEE\Vite\Tracy\VitePanel;
use Nette;
use Nette\DI\CompilerExtension;
use Nette\DI\Definitions;
use Nette\Schema;
use Tracy;

/**
* @property \stdClass $config
*/
final class Extension extends CompilerExtension
{

public function getConfigSchema(): Schema\Schema
{
return Schema\Expect::structure([
'server' => Schema\Expect::string('http://localhost:3000'),
'debugMode' => Schema\Expect::bool($this->getContainerBuilder()->parameters['debugMode'] ?? false),
'manifestFile' => Schema\Expect::string(),
'filterName' => Schema\Expect::string('asset'), // empty string is for disabled
'templateProperty' => Schema\Expect::string('_vite'), // empty string is for disabled
'wwwDir' => Schema\Expect::string($this->getContainerBuilder()->parameters['wwwDir'] ?? getcwd()),
'basePath' => Schema\Expect::string(),
]);
}


public function loadConfiguration(): void
{
$this->buildViteService();
$this->buildFilter();
}


private function buildViteService(): void
{
$manifestFile = $this->prepareManifestPath();
$this->getContainerBuilder()->addDefinition($this->prefix('service'))
->setFactory(Service::class)
->setArguments([
'viteServer' => $this->config->server,
'manifestFile' => $manifestFile,
'debugMode' => $this->config->debugMode,
'basePath' => $this->prepareBasePath($manifestFile),
]);
}


private function buildFilter(): void
{
$this->getContainerBuilder()->addDefinition($this->prefix('assetFilter'))
->setFactory(AssetFilter::class)
->setAutowired(false);
}


public function beforeCompile(): void
{
$builder = $this->getContainerBuilder();

$templateFactoryDefinition = $builder->getDefinition('latte.templateFactory');
assert($templateFactoryDefinition instanceof Definitions\ServiceDefinition);

if ($this->config->templateProperty !== '') {
$templateFactoryDefinition->addSetup(
new Nette\DI\Definitions\Statement('$onCreate[]', [
new Definitions\Statement([
self::class,
'prepareTemplate',
], [$this->config->templateProperty, $builder->getDefinition($this->prefix('service'))]),
]),
);
}

if ($this->config->filterName !== '' && $builder->hasDefinition('latte.latteFactory')) {
$definition = $builder->getDefinition('latte.latteFactory');
assert($definition instanceof Definitions\FactoryDefinition);
$definition->getResultDefinition()
->addSetup('addFilter', [
$this->config->filterName,
$builder->getDefinition($this->prefix('assetFilter')),
]);
}

$tracyClass = Tracy\Bar::class;
if ($this->config->debugMode && $builder->getByType($tracyClass)) {
$definition = $this->getContainerBuilder()
->getDefinition($this->prefix('service'));
assert($definition instanceof Definitions\ServiceDefinition);
$definition->addSetup("@$tracyClass::addPanel", [
new Definitions\Statement(VitePanel::class),
]);
}
}


public static function prepareTemplate(string $propertyName, Service $service): \Closure
{
return static function (Nette\Application\UI\Template $template) use ($propertyName, $service): void {
$template->{$propertyName} = $service;
};
}


private function prepareManifestPath(): string
{
if ($this->config->manifestFile === null) {
return $this->automaticSearchManifestFile();
}

$manifestFile = $this->config->manifestFile;
if (!is_file($manifestFile)) {
$newPath = $this->config->wwwDir . DIRECTORY_SEPARATOR . ltrim($manifestFile, '/\\');
if (!is_file($newPath)) {
throw new ManifestFileDoesNotExistsException(sprintf('Found here "%s" or "%s".', $manifestFile, $newPath));
}

$manifestFile = $newPath;
}

return Nette\Safe::realpath($manifestFile);
}


private function prepareBasePath(string $manifestFile): string
{
if ($this->config->basePath === null) {
return str_replace(Nette\Safe::realpath($this->config->wwwDir), '', dirname($manifestFile)) . '/';
}

return $this->config->basePath;
}


private function automaticSearchManifestFile(): string
{
$finder = Nette\Utils\Finder::findFiles('manifest.json')->from($this->config->wwwDir);
$files = [];
foreach ($finder as $file) {
$files[] = $file->getPathname();
}
if ($files === []) {
throw new ManifestFileDoesNotExistsException(sprintf('Define path to manifest.json, because automatic search found nothing in "%s".', $this->config->wwwDir));
} elseif (count($files) > 1) {
throw new ManifestFileDoesNotExistsException(sprintf('Define path to manifest.json, because automatic search found many manifest.json files %s.', implode(', ', $files)));
}

return reset($files);
}

}
104 changes: 104 additions & 0 deletions Vite/src/Service.php
@@ -0,0 +1,104 @@
<?php declare(strict_types=1);

namespace MMEE\Vite;

use Nette\Http\Request;
use Nette\Utils\FileSystem;
use Nette\Utils\Html;
use Nette\Utils\Json;

final class Service
{
private string $viteServer;

private string $manifestFile;

private bool $debugMode;

private string $basePath;

private Request $httpRequest;


public function __construct(
string $viteServer,
string $manifestFile,
bool $debugMode,
string $basePath,
Request $httpRequest
)
{
$this->viteServer = $viteServer;
$this->manifestFile = $manifestFile;
$this->debugMode = $debugMode;
$this->basePath = $basePath;
$this->httpRequest = $httpRequest;
}


public function getAsset(string $entrypoint): string
{
if ($this->isEnabled()) {
$baseUrl = $this->viteServer . '/';
$asset = $entrypoint;
} else {
$baseUrl = $this->basePath;
$asset = '';

if (file_exists($this->manifestFile)) {
$manifest = Json::decode(FileSystem::read($this->manifestFile), Json::FORCE_ARRAY);
$asset = $manifest[$entrypoint]['file'];
} else {
trigger_error('Missing manifest file: ' . $this->manifestFile, E_USER_WARNING);
}
}

return $baseUrl . $asset;
}


/**
* @return array<string, string>
*/
public function getCssAssets(string $entrypoint): array
{
$assets = [];

if (!$this->isEnabled()) {
if (file_exists($this->manifestFile)) {
$manifest = Json::decode(FileSystem::read($this->manifestFile), Json::FORCE_ARRAY);
$assets = $manifest[$entrypoint]['css'] ?? [];
} else {
trigger_error('Missing manifest file: ' . $this->manifestFile, E_USER_WARNING);
}
}

return $assets;
}


public function isEnabled(): bool
{
return $this->debugMode && $this->httpRequest->getCookie('netteVite') === 'true';
}


public function printTags(string $entrypoint): void
{
$scripts = [$this->getAsset($entrypoint)];
$styles = $this->getCssAssets($entrypoint);

if ($this->isEnabled()) {
echo Html::el('script')->type('module')->src($this->viteServer . '/' . '@vite/client');
}

foreach ($styles as $path) {
echo Html::el('link')->rel('stylesheet')->href($path);
}

foreach ($scripts as $path) {
echo Html::el('script')->type('module')->src($path);
}
}

}
2 changes: 0 additions & 2 deletions app/Tracy/Vite/Vite.html → Vite/src/Tracy/Vite.html
Expand Up @@ -34,8 +34,6 @@

const element = document.querySelector('#tracy-debug [data-action="netteVite"]')

console.log(getCookie('netteVite'))

if (!getCookie('netteVite')) {
element.style.opacity = '40%'
}
Expand Down
21 changes: 21 additions & 0 deletions Vite/src/Tracy/VitePanel.php
@@ -0,0 +1,21 @@
<?php declare(strict_types=1);

namespace MMEE\Vite\Tracy;

use Nette\Safe;
use Tracy;

final class VitePanel implements Tracy\IBarPanel
{
public function getTab()
{
return Safe::file_get_contents(__DIR__ . '/Vite.html');
}


public function getPanel()
{
return '';
}

}
21 changes: 0 additions & 21 deletions app/Latte/AssetFilter.php

This file was deleted.

7 changes: 0 additions & 7 deletions app/Presenters/BasePresenter.php
Expand Up @@ -13,12 +13,5 @@
*/
abstract class BasePresenter extends Nette\Application\UI\Presenter
{
public function __construct(
private Vite $vite,
) {}

public function beforeRender(): void
{
$this->template->vite = $this->vite;
}
}