[Asset] Adding a new version strategy that reads from a manifest JSON file
This commit is contained in:
parent
bafa8e29e0
commit
07fec2bbad
@ -37,5 +37,7 @@ return PhpCsFixer\Config::create()
|
||||
->notPath('Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_name_entry_label.html.php')
|
||||
// explicit heredoc test
|
||||
->notPath('Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Resources/views/translation.html.php')
|
||||
// purposefully invalid JSON
|
||||
->notPath('Symfony/Component/Asset/Tests/fixtures/manifest-invalid.json')
|
||||
)
|
||||
;
|
||||
|
@ -4,6 +4,8 @@ CHANGELOG
|
||||
3.3.0
|
||||
-----
|
||||
|
||||
* Added a new new version strategy option called json_manifest_path
|
||||
that allows you to use the `JsonManifestVersionStrategy`.
|
||||
* Added support for the `controller.service_arguments` tag, for injecting services into controllers' actions
|
||||
* Deprecated `cache:clear` with warmup (always call it with `--no-warmup`)
|
||||
* Deprecated the "framework.trusted_proxies" configuration option and the corresponding "kernel.trusted_proxies" parameter
|
||||
|
@ -551,6 +551,7 @@ class Configuration implements ConfigurationInterface
|
||||
->scalarNode('version_strategy')->defaultNull()->end()
|
||||
->scalarNode('version')->defaultNull()->end()
|
||||
->scalarNode('version_format')->defaultValue('%%s?%%s')->end()
|
||||
->scalarNode('json_manifest_path')->defaultNull()->end()
|
||||
->scalarNode('base_path')->defaultValue('')->end()
|
||||
->arrayNode('base_urls')
|
||||
->requiresAtLeastOneElement()
|
||||
@ -567,6 +568,18 @@ class Configuration implements ConfigurationInterface
|
||||
})
|
||||
->thenInvalid('You cannot use both "version_strategy" and "version" at the same time under "assets".')
|
||||
->end()
|
||||
->validate()
|
||||
->ifTrue(function ($v) {
|
||||
return isset($v['version_strategy']) && isset($v['json_manifest_path']);
|
||||
})
|
||||
->thenInvalid('You cannot use both "version_strategy" and "json_manifest_path" at the same time under "assets".')
|
||||
->end()
|
||||
->validate()
|
||||
->ifTrue(function ($v) {
|
||||
return isset($v['version']) && isset($v['json_manifest_path']);
|
||||
})
|
||||
->thenInvalid('You cannot use both "version" and "json_manifest_path" at the same time under "assets".')
|
||||
->end()
|
||||
->fixXmlConfig('package')
|
||||
->children()
|
||||
->arrayNode('packages')
|
||||
@ -582,6 +595,7 @@ class Configuration implements ConfigurationInterface
|
||||
->end()
|
||||
->end()
|
||||
->scalarNode('version_format')->defaultNull()->end()
|
||||
->scalarNode('json_manifest_path')->defaultNull()->end()
|
||||
->scalarNode('base_path')->defaultValue('')->end()
|
||||
->arrayNode('base_urls')
|
||||
->requiresAtLeastOneElement()
|
||||
@ -598,6 +612,18 @@ class Configuration implements ConfigurationInterface
|
||||
})
|
||||
->thenInvalid('You cannot use both "version_strategy" and "version" at the same time under "assets" packages.')
|
||||
->end()
|
||||
->validate()
|
||||
->ifTrue(function ($v) {
|
||||
return isset($v['version_strategy']) && isset($v['json_manifest_path']);
|
||||
})
|
||||
->thenInvalid('You cannot use both "version_strategy" and "json_manifest_path" at the same time under "assets" packages.')
|
||||
->end()
|
||||
->validate()
|
||||
->ifTrue(function ($v) {
|
||||
return isset($v['version']) && isset($v['json_manifest_path']);
|
||||
})
|
||||
->thenInvalid('You cannot use both "version" and "json_manifest_path" at the same time under "assets" packages.')
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
|
@ -810,7 +810,7 @@ class FrameworkExtension extends Extension
|
||||
if ($config['version_strategy']) {
|
||||
$defaultVersion = new Reference($config['version_strategy']);
|
||||
} else {
|
||||
$defaultVersion = $this->createVersion($container, $config['version'], $config['version_format'], '_default');
|
||||
$defaultVersion = $this->createVersion($container, $config['version'], $config['version_format'], $config['json_manifest_path'], '_default');
|
||||
}
|
||||
|
||||
$defaultPackage = $this->createPackageDefinition($config['base_path'], $config['base_urls'], $defaultVersion);
|
||||
@ -820,11 +820,14 @@ class FrameworkExtension extends Extension
|
||||
foreach ($config['packages'] as $name => $package) {
|
||||
if (null !== $package['version_strategy']) {
|
||||
$version = new Reference($package['version_strategy']);
|
||||
} elseif (!array_key_exists('version', $package)) {
|
||||
} elseif (!array_key_exists('version', $package) && null === $package['json_manifest_path']) {
|
||||
// if neither version nor json_manifest_path are specified, use the default
|
||||
$version = $defaultVersion;
|
||||
} else {
|
||||
// let format fallback to main version_format
|
||||
$format = $package['version_format'] ?: $config['version_format'];
|
||||
$version = $this->createVersion($container, $package['version'], $format, $name);
|
||||
$version = isset($package['version']) ? $package['version'] : null;
|
||||
$version = $this->createVersion($container, $version, $format, $package['json_manifest_path'], $name);
|
||||
}
|
||||
|
||||
$container->setDefinition('assets._package_'.$name, $this->createPackageDefinition($package['base_path'], $package['base_urls'], $version));
|
||||
@ -856,20 +859,29 @@ class FrameworkExtension extends Extension
|
||||
return $package;
|
||||
}
|
||||
|
||||
private function createVersion(ContainerBuilder $container, $version, $format, $name)
|
||||
private function createVersion(ContainerBuilder $container, $version, $format, $jsonManifestPath, $name)
|
||||
{
|
||||
if (null === $version) {
|
||||
return new Reference('assets.empty_version_strategy');
|
||||
// Configuration prevents $version and $jsonManifestPath from being set
|
||||
if (null !== $version) {
|
||||
$def = new ChildDefinition('assets.static_version_strategy');
|
||||
$def
|
||||
->replaceArgument(0, $version)
|
||||
->replaceArgument(1, $format)
|
||||
;
|
||||
$container->setDefinition('assets._version_'.$name, $def);
|
||||
|
||||
return new Reference('assets._version_'.$name);
|
||||
}
|
||||
|
||||
$def = new ChildDefinition('assets.static_version_strategy');
|
||||
$def
|
||||
->replaceArgument(0, $version)
|
||||
->replaceArgument(1, $format)
|
||||
;
|
||||
$container->setDefinition('assets._version_'.$name, $def);
|
||||
if (null !== $jsonManifestPath) {
|
||||
$def = new ChildDefinition('assets.json_manifest_version_strategy');
|
||||
$def->replaceArgument(0, $jsonManifestPath);
|
||||
$container->setDefinition('assets._version_'.$name, $def);
|
||||
|
||||
return new Reference('assets._version_'.$name);
|
||||
return new Reference('assets._version_'.$name);
|
||||
}
|
||||
|
||||
return new Reference('assets.empty_version_strategy');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -39,6 +39,10 @@
|
||||
|
||||
<service id="assets.empty_version_strategy" class="Symfony\Component\Asset\VersionStrategy\EmptyVersionStrategy" public="false" />
|
||||
|
||||
<service id="assets.json_manifest_version_strategy" class="Symfony\Component\Asset\VersionStrategy\JsonManifestVersionStrategy" abstract="true" public="false">
|
||||
<argument /> <!-- manifest path -->
|
||||
</service>
|
||||
|
||||
<service id="assets.preload_manager" class="Symfony\Component\Asset\Preload\PreloadManager" public="false" />
|
||||
|
||||
<service id="asset.preload_listener" class="Symfony\Component\Asset\EventListener\PreloadListener">
|
||||
|
@ -131,6 +131,7 @@
|
||||
<xsd:attribute name="version-strategy" type="xsd:string" />
|
||||
<xsd:attribute name="version" type="xsd:string" />
|
||||
<xsd:attribute name="version-format" type="xsd:string" />
|
||||
<xsd:attribute name="json-manifest-path" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="package">
|
||||
@ -143,6 +144,7 @@
|
||||
<xsd:attribute name="version-strategy" type="xsd:string" />
|
||||
<xsd:attribute name="version" type="xsd:string" />
|
||||
<xsd:attribute name="version-format" type="xsd:string" />
|
||||
<xsd:attribute name="json-manifest-path" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="templating">
|
||||
|
@ -14,6 +14,7 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Configuration;
|
||||
use Symfony\Bundle\FullStack;
|
||||
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
|
||||
use Symfony\Component\Config\Definition\Processor;
|
||||
|
||||
class ConfigurationTest extends TestCase
|
||||
@ -120,54 +121,67 @@ class ConfigurationTest extends TestCase
|
||||
'base_path' => '',
|
||||
'base_urls' => array(),
|
||||
'packages' => array(),
|
||||
'json_manifest_path' => null,
|
||||
);
|
||||
|
||||
$this->assertEquals($defaultConfig, $config['assets']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
|
||||
* @expectedExceptionMessage You cannot use both "version_strategy" and "version" at the same time under "assets".
|
||||
* @dataProvider provideInvalidAssetConfigurationTests
|
||||
*/
|
||||
public function testInvalidVersionStrategy()
|
||||
public function testInvalidAssetsConfiguration(array $assetConfig, $expectedMessage)
|
||||
{
|
||||
$this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}(
|
||||
InvalidConfigurationException::class,
|
||||
$expectedMessage
|
||||
);
|
||||
if (method_exists($this, 'expectExceptionMessage')) {
|
||||
$this->expectExceptionMessage($expectedMessage);
|
||||
}
|
||||
|
||||
$processor = new Processor();
|
||||
$configuration = new Configuration(true);
|
||||
$processor->processConfiguration($configuration, array(
|
||||
array(
|
||||
'assets' => array(
|
||||
'base_urls' => '//example.com',
|
||||
'version' => 1,
|
||||
'version_strategy' => 'foo',
|
||||
array(
|
||||
'assets' => $assetConfig,
|
||||
),
|
||||
),
|
||||
));
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
|
||||
* @expectedExceptionMessage You cannot use both "version_strategy" and "version" at the same time under "assets" packages.
|
||||
*/
|
||||
public function testInvalidPackageVersionStrategy()
|
||||
public function provideInvalidAssetConfigurationTests()
|
||||
{
|
||||
$processor = new Processor();
|
||||
$configuration = new Configuration(true);
|
||||
|
||||
$processor->processConfiguration($configuration, array(
|
||||
array(
|
||||
'assets' => array(
|
||||
'base_urls' => '//example.com',
|
||||
'version' => 1,
|
||||
'packages' => array(
|
||||
'foo' => array(
|
||||
'base_urls' => '//example.com',
|
||||
'version' => 1,
|
||||
'version_strategy' => 'foo',
|
||||
),
|
||||
),
|
||||
// helper to turn config into embedded package config
|
||||
$createPackageConfig = function (array $packageConfig) {
|
||||
return array(
|
||||
'base_urls' => '//example.com',
|
||||
'version' => 1,
|
||||
'packages' => array(
|
||||
'foo' => $packageConfig,
|
||||
),
|
||||
),
|
||||
));
|
||||
);
|
||||
};
|
||||
|
||||
$config = array(
|
||||
'version' => 1,
|
||||
'version_strategy' => 'foo',
|
||||
);
|
||||
yield array($config, 'You cannot use both "version_strategy" and "version" at the same time under "assets".');
|
||||
yield array($createPackageConfig($config), 'You cannot use both "version_strategy" and "version" at the same time under "assets" packages.');
|
||||
|
||||
$config = array(
|
||||
'json_manifest_path' => '/foo.json',
|
||||
'version_strategy' => 'foo',
|
||||
);
|
||||
yield array($config, 'You cannot use both "version_strategy" and "json_manifest_path" at the same time under "assets".');
|
||||
yield array($createPackageConfig($config), 'You cannot use both "version_strategy" and "json_manifest_path" at the same time under "assets" packages.');
|
||||
|
||||
$config = array(
|
||||
'json_manifest_path' => '/foo.json',
|
||||
'version' => '1',
|
||||
);
|
||||
yield array($config, 'You cannot use both "version" and "json_manifest_path" at the same time under "assets".');
|
||||
yield array($createPackageConfig($config), 'You cannot use both "version" and "json_manifest_path" at the same time under "assets" packages.');
|
||||
}
|
||||
|
||||
protected static function getBundleDefaultConfig()
|
||||
@ -274,6 +288,7 @@ class ConfigurationTest extends TestCase
|
||||
'base_path' => '',
|
||||
'base_urls' => array(),
|
||||
'packages' => array(),
|
||||
'json_manifest_path' => null,
|
||||
),
|
||||
'cache' => array(
|
||||
'pools' => array(),
|
||||
|
@ -24,6 +24,9 @@ $container->loadFromExtension('framework', array(
|
||||
'base_urls' => array('https://bar2.example.com'),
|
||||
'version_strategy' => 'assets.custom_version_strategy',
|
||||
),
|
||||
'json_manifest_strategy' => array(
|
||||
'json_manifest_path' => '/path/to/manifest.json',
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
@ -21,6 +21,7 @@
|
||||
<framework:package name="bar_version_strategy" version-strategy="assets.custom_version_strategy">
|
||||
<framework:base-url>https://bar_version_strategy.example.com</framework:base-url>
|
||||
</framework:package>
|
||||
<framework:package name="json_manifest_strategy" json-manifest-path="/path/to/manifest.json" />
|
||||
</framework:assets>
|
||||
</framework:config>
|
||||
</container>
|
||||
|
@ -17,3 +17,5 @@ framework:
|
||||
bar_version_strategy:
|
||||
base_urls: ["https://bar_version_strategy.example.com"]
|
||||
version_strategy: assets.custom_version_strategy
|
||||
json_manifest_strategy:
|
||||
json_manifest_path: '/path/to/manifest.json'
|
||||
|
@ -374,7 +374,7 @@ abstract class FrameworkExtensionTest extends TestCase
|
||||
|
||||
// packages
|
||||
$packages = $packages->getArgument(1);
|
||||
$this->assertCount(5, $packages);
|
||||
$this->assertCount(6, $packages);
|
||||
|
||||
$package = $container->getDefinition((string) $packages['images_path']);
|
||||
$this->assertPathPackage($container, $package, '/foo', 'SomeVersionScheme', '%%s?version=%%s');
|
||||
@ -390,6 +390,11 @@ abstract class FrameworkExtensionTest extends TestCase
|
||||
|
||||
$package = $container->getDefinition((string) $packages['bar_version_strategy']);
|
||||
$this->assertEquals('assets.custom_version_strategy', (string) $package->getArgument(1));
|
||||
|
||||
$package = $container->getDefinition((string) $packages['json_manifest_strategy']);
|
||||
$versionStrategy = $container->getDefinition((string) $package->getArgument(1));
|
||||
$this->assertEquals('assets.json_manifest_version_strategy', $versionStrategy->getParent());
|
||||
$this->assertEquals('/path/to/manifest.json', $versionStrategy->getArgument(0));
|
||||
}
|
||||
|
||||
public function testAssetsDefaultVersionStrategyAsService()
|
||||
|
@ -1,6 +1,11 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
3.3.0
|
||||
-----
|
||||
* Added `JsonManifestVersionStrategy` as a way to read final,
|
||||
versioned paths from a JSON manifest file.
|
||||
|
||||
2.7.0
|
||||
-----
|
||||
|
||||
|
@ -0,0 +1,63 @@
|
||||
<?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\Component\Asset\Tests\VersionStrategy;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Asset\VersionStrategy\JsonManifestVersionStrategy;
|
||||
|
||||
class JsonManifestVersionStrategyTest extends TestCase
|
||||
{
|
||||
public function testGetVersion()
|
||||
{
|
||||
$strategy = $this->createStrategy('manifest-valid.json');
|
||||
|
||||
$this->assertEquals('main.123abc.js', $strategy->getVersion('main.js'));
|
||||
}
|
||||
|
||||
public function testApplyVersion()
|
||||
{
|
||||
$strategy = $this->createStrategy('manifest-valid.json');
|
||||
|
||||
$this->assertEquals('css/styles.555def.css', $strategy->getVersion('css/styles.css'));
|
||||
}
|
||||
|
||||
public function testApplyVersionWhenKeyDoesNotExistInManifest()
|
||||
{
|
||||
$strategy = $this->createStrategy('manifest-valid.json');
|
||||
|
||||
$this->assertEquals('css/other.css', $strategy->getVersion('css/other.css'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
*/
|
||||
public function testMissingManifestFileThrowsException()
|
||||
{
|
||||
$strategy = $this->createStrategy('non-existent-file.json');
|
||||
$strategy->getVersion('main.js');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
* @expectedExceptionMessage Error parsing JSON
|
||||
*/
|
||||
public function testManifestFileWithBadJSONThrowsException()
|
||||
{
|
||||
$strategy = $this->createStrategy('manifest-invalid.json');
|
||||
$strategy->getVersion('main.js');
|
||||
}
|
||||
|
||||
private function createStrategy($manifestFilename)
|
||||
{
|
||||
return new JsonManifestVersionStrategy(__DIR__.'/../fixtures/'.$manifestFilename);
|
||||
}
|
||||
}
|
4
src/Symfony/Component/Asset/Tests/fixtures/manifest-invalid.json
vendored
Normal file
4
src/Symfony/Component/Asset/Tests/fixtures/manifest-invalid.json
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"main.js": main.123abc.js",
|
||||
"css/styles.css": "css/styles.555def.css"
|
||||
}
|
4
src/Symfony/Component/Asset/Tests/fixtures/manifest-valid.json
vendored
Normal file
4
src/Symfony/Component/Asset/Tests/fixtures/manifest-valid.json
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"main.js": "main.123abc.js",
|
||||
"css/styles.css": "css/styles.555def.css"
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
<?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\Component\Asset\VersionStrategy;
|
||||
|
||||
/**
|
||||
* Reads the versioned path of an asset from a JSON manifest file.
|
||||
*
|
||||
* For example, the manifest file might look like this:
|
||||
* {
|
||||
* "main.js": "main.abc123.js",
|
||||
* "css/styles.css": "css/styles.555abc.css"
|
||||
* }
|
||||
*
|
||||
* You could then ask for the version of "main.js" or "css/styles.css".
|
||||
*/
|
||||
class JsonManifestVersionStrategy implements VersionStrategyInterface
|
||||
{
|
||||
private $manifestPath;
|
||||
private $manifestData;
|
||||
|
||||
/**
|
||||
* @param string $manifestPath Absolute path to the manifest file
|
||||
*/
|
||||
public function __construct($manifestPath)
|
||||
{
|
||||
$this->manifestPath = $manifestPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* With a manifest, we don't really know or care about what
|
||||
* the version is. Instead, this returns the path to the
|
||||
* versioned file.
|
||||
*/
|
||||
public function getVersion($path)
|
||||
{
|
||||
return $this->applyVersion($path);
|
||||
}
|
||||
|
||||
public function applyVersion($path)
|
||||
{
|
||||
return $this->getManifestPath($path) ?: $path;
|
||||
}
|
||||
|
||||
private function getManifestPath($path)
|
||||
{
|
||||
if (null === $this->manifestData) {
|
||||
if (!file_exists($this->manifestPath)) {
|
||||
throw new \RuntimeException(sprintf('Asset manifest file "%s" does not exist.', $this->manifestPath));
|
||||
}
|
||||
|
||||
$this->manifestData = json_decode(file_get_contents($this->manifestPath), true);
|
||||
if (0 < json_last_error()) {
|
||||
throw new \RuntimeException(sprintf('Error parsing JSON from asset manifest file "%s" - %s', $this->manifestPath, json_last_error_msg()));
|
||||
}
|
||||
}
|
||||
|
||||
return isset($this->manifestData[$path]) ? $this->manifestData[$path] : null;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user