Files
archived-stimulus-bundle/src/AssetMapper/StimulusLoaderJavaScriptCompiler.php
2026-02-03 22:36:24 +01:00

134 lines
5.6 KiB
PHP

<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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\AssetDependency;
use Symfony\Component\AssetMapper\AssetMapperInterface;
use Symfony\Component\AssetMapper\Compiler\AssetCompilerInterface;
use Symfony\Component\AssetMapper\MappedAsset;
use Symfony\Component\Filesystem\Path;
/**
* Compiles the loader.js file to dynamically import the controllers.
*
* @internal
*
* @author Ryan Weaver <ryan@symfonycasts.com>
*/
class StimulusLoaderJavaScriptCompiler implements AssetCompilerInterface
{
public function __construct(
private ControllersMapGenerator $controllersMapGenerator,
private bool $isDebug,
) {
}
public function supports(MappedAsset $asset): bool
{
return $asset->sourcePath === realpath(__DIR__.'/../../assets/dist/controllers.js');
}
public function compile(string $content, MappedAsset $asset, AssetMapperInterface $assetMapper): string
{
$importLines = [];
$eagerControllerParts = [];
$lazyControllers = [];
// add file dependencies so the cache rebuilds
$asset->addFileDependency($this->controllersMapGenerator->getControllersJsonPath());
foreach ($this->controllersMapGenerator->getControllerPaths() as $controllerDir) {
$asset->addFileDependency($controllerDir);
}
foreach ($this->controllersMapGenerator->getControllersMap() as $name => $mappedControllerAsset) {
// @legacy: backwards compatibility with Symfony 6.3
if (class_exists(AssetDependency::class)) {
$loaderPublicPath = $asset->publicPathWithoutDigest;
$controllerPublicPath = $mappedControllerAsset->asset->publicPathWithoutDigest;
$relativeImportPath = Path::makeRelative($controllerPublicPath, \dirname($loaderPublicPath));
} else {
$relativeImportPath = Path::makeRelative($mappedControllerAsset->asset->sourcePath, \dirname($asset->sourcePath));
}
$relativeImportPath = json_encode($relativeImportPath, \JSON_THROW_ON_ERROR | \JSON_UNESCAPED_SLASHES);
/*
* The AssetDependency will already be added by AssetMapper itself when
* it processes this file. However, due to the "stimulusFetch: 'lazy'"
* that may appear inside the controllers, this file is dependent on
* the "contents" of each controller. So, we add the dependency here
* and mark it as a "content" dependency so that this file's contents
* will be recalculated when the contents of any controller changes.
*/
if (class_exists(AssetDependency::class)) {
// @legacy: Backwards compatibility with Symfony 6.3
$asset->addDependency(new AssetDependency(
$mappedControllerAsset->asset,
$mappedControllerAsset->isLazy,
true,
));
} else {
$asset->addDependency($mappedControllerAsset->asset);
}
$autoImportPaths = [];
foreach ($mappedControllerAsset->autoImports as $autoImport) {
if ($autoImport->isBareImport) {
$autoImportPaths[] = json_encode($autoImport->path, \JSON_THROW_ON_ERROR | \JSON_UNESCAPED_SLASHES);
} else {
$autoImportPaths[] = json_encode(Path::makeRelative($autoImport->path, \dirname($asset->sourcePath)), \JSON_THROW_ON_ERROR | \JSON_UNESCAPED_SLASHES);
}
}
if ($mappedControllerAsset->isLazy) {
if (!$mappedControllerAsset->autoImports) {
$lazyControllers[] = \sprintf('%s: () => import(%s)', json_encode($name), $relativeImportPath);
} else {
// import $relativeImportPath and also the auto-imports
// and use a Promise.all() to wait for all of them
$lazyControllers[] = \sprintf('%s: () => Promise.all([import(%s), %s]).then((ret) => ret[0])', json_encode($name), $relativeImportPath, implode(', ', array_map(static fn ($path) => "import($path)", $autoImportPaths)));
}
continue;
}
$controllerNameForVariable = \sprintf('controller_%s', \count($eagerControllerParts));
$importLines[] = \sprintf(
'import %s from %s;',
$controllerNameForVariable,
$relativeImportPath
);
foreach ($autoImportPaths as $autoImportRelativePath) {
$importLines[] = \sprintf(
'import %s;',
$autoImportRelativePath
);
}
$eagerControllerParts[] = \sprintf('"%s": %s', $name, $controllerNameForVariable);
}
$importCode = implode("\n", $importLines);
$eagerControllersJson = \sprintf('{%s}', implode(', ', $eagerControllerParts));
$lazyControllersExpression = \sprintf('{%s}', implode(', ', $lazyControllers));
$isDebugString = $this->isDebug ? 'true' : 'false';
return <<<EOF
$importCode
export const eagerControllers = $eagerControllersJson;
export const lazyControllers = $lazyControllersExpression;
export const isApplicationDebug = $isDebugString;
EOF;
}
}