Skip to content

Commit

Permalink
Adding support for AssetMapper 6.4
Browse files Browse the repository at this point in the history
  • Loading branch information
weaverryan committed Nov 2, 2023
1 parent 52dc225 commit 05bcb98
Show file tree
Hide file tree
Showing 37 changed files with 558 additions and 125 deletions.
16 changes: 10 additions & 6 deletions src/React/src/AssetMapper/ReactControllerLoaderAssetCompiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@

namespace Symfony\UX\React\AssetMapper;

use Symfony\Component\AssetMapper\AssetDependency;
use Symfony\Component\AssetMapper\AssetMapperInterface;
use Symfony\Component\AssetMapper\Compiler\AssetCompilerInterface;
use Symfony\Component\AssetMapper\Compiler\AssetCompilerPathResolverTrait;
use Symfony\Component\AssetMapper\MappedAsset;
use Symfony\Component\Filesystem\Path;
use Symfony\Component\Finder\Finder;

/**
Expand All @@ -24,8 +25,6 @@
*/
class ReactControllerLoaderAssetCompiler implements AssetCompilerInterface
{
use AssetCompilerPathResolverTrait;

public function __construct(
private string $controllerPath,
private array $nameGlobs,
Expand All @@ -41,10 +40,15 @@ public function compile(string $content, MappedAsset $asset, AssetMapperInterfac
{
$importLines = [];
$componentParts = [];
$loaderPublicPath = $asset->publicPathWithoutDigest;
foreach ($this->findControllerAssets($assetMapper) as $name => $mappedAsset) {
$controllerPublicPath = $mappedAsset->publicPathWithoutDigest;
$relativeImportPath = $this->createRelativePath($loaderPublicPath, $controllerPublicPath);
// @legacy: backwards compatibility with Symfony 6.3
if (class_exists(AssetDependency::class)) {
$controllerPublicPath = $mappedAsset->publicPathWithoutDigest;
$loaderPublicPath = $asset->publicPathWithoutDigest;
$relativeImportPath = Path::makeRelative($controllerPublicPath, \dirname($loaderPublicPath));
} else {
$relativeImportPath = Path::makeRelative($mappedAsset->sourcePath, \dirname($asset->sourcePath));
}

$controllerNameForVariable = sprintf('component_%s', \count($componentParts));

Expand Down
30 changes: 13 additions & 17 deletions src/React/src/DependencyInjection/ReactExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
namespace Symfony\UX\React\DependencyInjection;

use Symfony\Component\AssetMapper\AssetMapperInterface;
use Symfony\Component\AssetMapper\Compiler\AssetCompilerPathResolverTrait;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
Expand Down Expand Up @@ -44,22 +43,19 @@ public function load(array $configs, ContainerBuilder $container)
->setPublic(false)
;

// on older versions, the absence of this trait will trigger an error if the service is loaded
if (trait_exists(AssetCompilerPathResolverTrait::class)) {
$container->setDefinition('react.asset_mapper.react_controller_loader_compiler', new Definition(ReactControllerLoaderAssetCompiler::class))
->setArguments([
$config['controllers_path'],
$config['name_glob'],
])
// run before the core JavaScript compiler
->addTag('asset_mapper.compiler', ['priority' => 100]);

$container->setDefinition('react.asset_mapper.replace_process_env_compiler', new Definition(ReactReplaceProcessEnvAssetCompiler::class))
->setArguments([
'%kernel.debug%',
])
->addTag('asset_mapper.compiler');
}
$container->setDefinition('react.asset_mapper.react_controller_loader_compiler', new Definition(ReactControllerLoaderAssetCompiler::class))
->setArguments([
$config['controllers_path'],
$config['name_glob'],
])
// run before the core JavaScript compiler
->addTag('asset_mapper.compiler', ['priority' => 100]);

$container->setDefinition('react.asset_mapper.replace_process_env_compiler', new Definition(ReactReplaceProcessEnvAssetCompiler::class))
->setArguments([
'%kernel.debug%',
])
->addTag('asset_mapper.compiler');
}

public function prepend(ContainerBuilder $container)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,15 @@ public function testCompileDynamicallyAddsContents()
if (str_contains($sourcePath, 'MyReactController')) {
return new MappedAsset(
'MyReactController.js',
'/project/assets/react/controllers/MyReactController.js',
publicPathWithoutDigest: '/assets/react/controllers/MyReactController.js',
);
}

if (str_contains($sourcePath, 'DeeperReactController')) {
return new MappedAsset(
'subdir/DeeperReactController.js',
'/project/assets/react/controllers/subdir/DeeperReactController.js',
publicPathWithoutDigest: '/assets/react/controllers/subdir/DeeperReactController.js',
);
}
Expand All @@ -50,7 +52,7 @@ public function testCompileDynamicallyAddsContents()
['*.js']
);

$loaderAsset = new MappedAsset('loader.js', publicPathWithoutDigest: '/assets/symfony/ux-react/loader.js');
$loaderAsset = new MappedAsset('loader.js', '/project/assets/vendor/StimulusBundle/loader.js', publicPathWithoutDigest: '/assets/symfony/ux-react/loader.js');
$startingContents = file_get_contents(__DIR__.'/../../assets/dist/loader.js');

$compiledContents = $compiler->compile($startingContents, $loaderAsset, $assetMapper);
Expand Down
3 changes: 2 additions & 1 deletion src/StimulusBundle/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"symfony/dependency-injection": "^5.4|^6.0|^7.0",
"symfony/finder": "^5.4|^6.0|^7.0",
"symfony/http-kernel": "^5.4|^6.0|^7.0",
"twig/twig": "^2.15.3|^3.4.3"
"twig/twig": "^2.15.3|^3.4.3",
"symfony/deprecation-contracts": "^2.0|^3.0"
},
"require-dev": {
"symfony/asset-mapper": "^6.3|^7.0",
Expand Down
10 changes: 10 additions & 0 deletions src/StimulusBundle/config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/

use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\UX\StimulusBundle\AssetMapper\AutoImportLocator;
use Symfony\UX\StimulusBundle\AssetMapper\ControllersMapGenerator;
use Symfony\UX\StimulusBundle\AssetMapper\StimulusLoaderJavaScriptCompiler;
use Symfony\UX\StimulusBundle\Helper\StimulusHelper;
Expand Down Expand Up @@ -64,6 +65,15 @@
service('stimulus.asset_mapper.ux_package_reader'),
abstract_arg('controller paths'),
abstract_arg('controllers_json_path'),
// @legacy - only allowing null for framework-bundle 6.3
service('stimulus.asset_mapper.auto_import_locator')->nullOnInvalid(),
])

// @legacy - is removed in 6.3
->set('stimulus.asset_mapper.auto_import_locator', AutoImportLocator::class)
->args([
service('asset_mapper.importmap.config_reader'),
service('asset_mapper'),
])

->set('stimulus.asset_mapper.loader_javascript_compiler', StimulusLoaderJavaScriptCompiler::class)
Expand Down
72 changes: 72 additions & 0 deletions src/StimulusBundle/src/AssetMapper/AutoImportLocator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\UX\StimulusBundle\AssetMapper;

use Symfony\Component\AssetMapper\AssetMapperInterface;
use Symfony\Component\AssetMapper\ImportMap\ImportMapConfigReader;
use Symfony\Component\AssetMapper\MappedAsset;
use Symfony\UX\StimulusBundle\Ux\UxPackageMetadata;

/**
* Finds the MappedAsset for an "autoimport" string.
*/
class AutoImportLocator
{
public function __construct(
private ImportMapConfigReader $importMapConfigReader,
private AssetMapperInterface $assetMapper,
) {
}

// parts of this method are duplicated & adapted from UxControllersTwigRuntime
public function locateAutoImport(string $path, UxPackageMetadata $packageMetadata): MappedControllerAutoImport
{
// see if this is a mapped asset path
if ($asset = $this->assetMapper->getAsset($path)) {
return new MappedControllerAutoImport($asset->sourcePath, false);
}

$slashPosition = strpos($path, '/');
if (false === $slashPosition) {
throw new \LogicException(sprintf('The autoimport "%s" is not valid.', $path));
}

$parts = explode('/', ltrim($path, '@'));
if (2 > \count($parts)) {
throw new \LogicException(sprintf('The autoimport "%s" is not valid.', $path));
}
$package = implode('/', \array_slice($parts, 0, 2));
$file = implode('/', \array_slice($parts, 2));

if ($package === $packageMetadata->packageName) {
// this is a file local to the ux package
$filePath = $packageMetadata->packageDirectory.'/'.$file;
if (!is_file($filePath)) {
throw new \LogicException(sprintf('An "autoimport" in "controllers.json" refers to "%s". This path could not be found in the asset mapper and the file "%s" does not exist in the package path "%s". And so, the file cannot be loaded.', $path, $filePath, $packageMetadata->packageDirectory));
}

$asset = $this->assetMapper->getAssetFromSourcePath($filePath);
if (!$asset) {
throw new \LogicException(sprintf('An "autoimport" in "controllers.json" refers to "%s". This file was found, but the path is not in the asset mapper. And so, the file cannot be loaded. This is a misconfiguration with the bundle providing this.', $path));
}

return new MappedControllerAutoImport($asset->sourcePath, false);
}

$entry = $this->importMapConfigReader->findRootImportMapEntry($path);
if (!$entry) {
throw new \LogicException(sprintf('The autoimport "%s" could not be found in importmap.php. Try running "importmap:require %s".', $path, $path));
}

return new MappedControllerAutoImport($path, true);
}
}
35 changes: 33 additions & 2 deletions src/StimulusBundle/src/AssetMapper/ControllersMapGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
namespace Symfony\UX\StimulusBundle\AssetMapper;

use Symfony\Component\AssetMapper\AssetMapperInterface;
use Symfony\Component\AssetMapper\ImportMap\ImportMapGenerator;
use Symfony\Component\Finder\Finder;
use Symfony\UX\StimulusBundle\Ux\UxPackageMetadata;
use Symfony\UX\StimulusBundle\Ux\UxPackageReader;

/**
Expand All @@ -31,6 +33,7 @@ public function __construct(
private UxPackageReader $uxPackageReader,
private array $controllerPaths,
private string $controllersJsonPath,
private ?AutoImportLocator $autoImportLocator = null,
) {
}

Expand Down Expand Up @@ -72,7 +75,8 @@ private function loadCustomControllers(): array
$name = str_replace(['_', '/'], ['-', '--'], $name);

$asset = $this->assetMapper->getAssetFromSourcePath($file->getRealPath());
$isLazy = preg_match('/\/\*\s*stimulusFetch:\s*\'lazy\'\s*\*\//i', $asset->content);
$content = $asset->content ?: file_get_contents($asset->sourcePath);
$isLazy = preg_match('/\/\*\s*stimulusFetch:\s*\'lazy\'\s*\*\//i', $content);

$controllersMap[$name] = new MappedControllerAsset($asset, $isLazy);
}
Expand Down Expand Up @@ -129,10 +133,37 @@ private function loadUxControllers(): array
throw new \RuntimeException(sprintf('Could not find an asset mapper path that points to the "%s" controller in package "%s", defined in controllers.json.', $controllerName, $packageMetadata->packageName));
}

$controllersMap[$controllerNormalizedName] = new MappedControllerAsset($asset, $lazy);
$autoImports = $this->collectAutoImports($localControllerConfig['autoimport'] ?? [], $packageMetadata);

$controllersMap[$controllerNormalizedName] = new MappedControllerAsset($asset, $lazy, $autoImports);
}
}

return $controllersMap;
}

/**
* @return MappedControllerAutoImport[]
*/
private function collectAutoImports(array $autoImports, UxPackageMetadata $currentPackageMetadata): array
{
// @legacy: Backwards compatibility with Symfony 6.3
if (!class_exists(ImportMapGenerator::class)) {
return [];
}
if (null === $this->autoImportLocator) {
throw new \InvalidArgumentException(sprintf('The "autoImportLocator" argument to "%s" is required when using AssetMapper 6.4', self::class));
}

$autoImportItems = [];
foreach ($autoImports as $path => $enabled) {
if (!$enabled) {
continue;
}

$autoImportItems[] = $this->autoImportLocator->locateAutoImport($path, $currentPackageMetadata);
}

return $autoImportItems;
}
}
4 changes: 4 additions & 0 deletions src/StimulusBundle/src/AssetMapper/MappedControllerAsset.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ class MappedControllerAsset
public function __construct(
public MappedAsset $asset,
public bool $isLazy,
/**
* @var MappedControllerAutoImport[]
*/
public array $autoImports = [],
) {
}
}
26 changes: 26 additions & 0 deletions src/StimulusBundle/src/AssetMapper/MappedControllerAutoImport.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\UX\StimulusBundle\AssetMapper;

/**
* @experimental
*
* @author Ryan Weaver <[email protected]>
*/
class MappedControllerAutoImport
{
public function __construct(
public string $path,
public bool $isBareImport
) {
}
}
Loading

0 comments on commit 05bcb98

Please sign in to comment.