Subida del módulo y tema de PrestaShop

This commit is contained in:
Kaloyan
2026-04-09 18:31:51 +02:00
parent 12c253296f
commit 16b3ff9424
39262 changed files with 7418797 additions and 0 deletions

View File

@@ -0,0 +1,168 @@
<?php
declare(strict_types=1);
/*
* This file is part of the FOSJsRoutingBundle package.
*
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\JsRoutingBundle\Command;
use FOS\JsRoutingBundle\Extractor\ExposedRoutesExtractorInterface;
use FOS\JsRoutingBundle\Response\RoutesResponse;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Serializer\SerializerInterface;
/**
* Dumps routes to the filesystem.
*
* @author Benjamin Dulau <benjamin.dulau@anonymation.com>
*/
#[AsCommand('fos:js-routing:dump', 'Dumps exposed routes to the filesystem')]
class DumpCommand extends Command
{
public function __construct(
private RoutesResponse $routesResponse,
private ExposedRoutesExtractorInterface $extractor,
private SerializerInterface $serializer,
private string $projectDir,
private ?string $requestContextBaseUrl = null,
) {
parent::__construct();
}
protected function configure(): void
{
$this
->setName('fos:js-routing:dump')
->setDescription('Dumps exposed routes to the filesystem')
->addOption(
'callback',
null,
InputOption::VALUE_REQUIRED,
'Callback function to pass the routes as an argument.',
'fos.Router.setData'
)
->addOption(
'format',
null,
InputOption::VALUE_REQUIRED,
'Format to output routes in. js to wrap the response in a callback, json for raw json output. Callback is ignored when format is json',
'js'
)
->addOption(
'target',
null,
InputOption::VALUE_OPTIONAL,
'Override the target file to dump routes in.'
)
->addOption(
'locale',
null,
InputOption::VALUE_OPTIONAL,
'Set locale to be used with JMSI18nRoutingBundle.',
''
)
->addOption(
'pretty-print',
'p',
InputOption::VALUE_NONE,
'Pretty print the JSON.'
)
->addOption(
'domain',
null,
InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
'Specify expose domain',
[]
)
;
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
if (!in_array($input->getOption('format'), ['js', 'json'])) {
$output->writeln('<error>Invalid format specified. Use js or json.</error>');
return 1;
}
$callback = $input->getOption('callback');
if (empty($callback)) {
$output->writeln('<error>If you include --callback it must not be empty. Do you perhaps want --format=json</error>');
return 1;
}
$output->writeln('Dumping exposed routes.');
$output->writeln('');
$this->doDump($input, $output);
return 0;
}
/**
* Performs the routes dump.
*/
private function doDump(InputInterface $input, OutputInterface $output): void
{
$domain = $input->getOption('domain');
$extractor = $this->extractor;
$serializer = $this->serializer;
$targetPath = $input->getOption('target') ?:
sprintf(
'%s/public/js/fos_js_routes%s.%s',
$this->projectDir,
empty($domain) ? '' : ('_'.implode('_', $domain)),
$input->getOption('format')
);
if (!is_dir($dir = dirname($targetPath))) {
$output->writeln('<info>[dir+]</info> '.$dir);
if (false === @mkdir($dir, 0777, true)) {
throw new \RuntimeException('Unable to create directory '.$dir);
}
}
$output->writeln('<info>[file+]</info> '.$targetPath);
$baseUrl = $this->requestContextBaseUrl ?? $this->extractor->getBaseUrl()
;
if ($input->getOption('pretty-print')) {
$params = ['json_encode_options' => JSON_PRETTY_PRINT];
} else {
$params = [];
}
$this->routesResponse->setBaseUrl($baseUrl);
$this->routesResponse->setRoutes($extractor->getRoutes());
$this->routesResponse->setPrefix($extractor->getPrefix($input->getOption('locale')));
$this->routesResponse->setHost($extractor->getHost());
$this->routesResponse->setPort($extractor->getPort());
$this->routesResponse->setScheme($extractor->getScheme());
$this->routesResponse->setLocale($input->getOption('locale'));
$this->routesResponse->setDomains($domain);
$content = $serializer->serialize($this->routesResponse, 'json', $params);
if ('js' == $input->getOption('format')) {
$content = sprintf('%s(%s);', $input->getOption('callback'), $content);
}
if (false === @file_put_contents($targetPath, $content)) {
throw new \RuntimeException('Unable to write file '.$targetPath);
}
}
}

View File

@@ -0,0 +1,123 @@
<?php
declare(strict_types=1);
/*
* This file is part of the FOSJsRoutingBundle package.
*
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\JsRoutingBundle\Command;
use FOS\JsRoutingBundle\Extractor\ExposedRoutesExtractorInterface;
use Symfony\Bundle\FrameworkBundle\Console\Helper\DescriptorHelper;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\RouterInterface;
/**
* A console command for retrieving information about exposed routes.
*
* @author William DURAND <william.durand1@gmail.com>
*/
#[AsCommand('fos:js-routing:debug', 'Displays currently exposed routes for an application')]
class RouterDebugExposedCommand extends Command
{
public function __construct(private ExposedRoutesExtractorInterface $extractor, private RouterInterface $router)
{
parent::__construct();
}
/**
* {@inheritdoc}
*/
protected function configure(): void
{
$this
->setDefinition([
new InputArgument('name', InputArgument::OPTIONAL, 'A route name'),
new InputOption('show-controllers', null, InputOption::VALUE_NONE, 'Show assigned controllers in overview'),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'),
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw route(s)'),
new InputOption('domain', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Specify expose domain', []),
])
->setName('fos:js-routing:debug')
->setDescription('Displays currently exposed routes for an application')
->setHelp(<<<EOF
The <info>fos:js-routing:debug</info> command displays an application's routes which will be available via JavaScript.
<info>php app/console fos:js-routing:debug</info>
You can alternatively specify a route name as an argument to get more info about that specific route:
<info>php app/console fos:js-routing:debug my_route</info>
EOF
)
;
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
if ($name = $input->getArgument('name')) {
/** @var Route $route */
$route = $this->router->getRouteCollection()->get($name);
if (!$route) {
throw new \InvalidArgumentException(sprintf('The route "%s" does not exist.', $name));
}
if (!$this->extractor->isRouteExposed($route, $name)) {
throw new \InvalidArgumentException(sprintf('The route "%s" was found, but it is not an exposed route.', $name));
}
$helper = new DescriptorHelper();
$helper->describe($output, $route, [
'format' => $input->getOption('format'),
'raw_text' => $input->getOption('raw'),
'show_controllers' => $input->getOption('show-controllers'),
]);
} else {
$helper = new DescriptorHelper();
$helper->describe($output, $this->getRoutes($input->getOption('domain')), [
'format' => $input->getOption('format'),
'raw_text' => $input->getOption('raw'),
'show_controllers' => $input->getOption('show-controllers'),
]);
}
return 0;
}
protected function getRoutes($domain = []): RouteCollection
{
$routes = $this->extractor->getRoutes();
if (empty($domain)) {
return $routes;
}
$targetRoutes = new RouteCollection();
foreach ($routes as $name => $route) {
$expose = $route->getOption('expose');
$expose = is_string($expose) ? ('true' === $expose ? 'default' : $expose) : 'default';
if (in_array($expose, $domain, true)) {
$targetRoutes->add($name, $route);
}
}
return $targetRoutes;
}
}

View File

@@ -0,0 +1,100 @@
<?php
declare(strict_types=1);
/*
* This file is part of the FOSJsRoutingBundle package.
*
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\JsRoutingBundle\Controller;
use FOS\JsRoutingBundle\Extractor\ExposedRoutesExtractorInterface;
use FOS\JsRoutingBundle\Response\RoutesResponse;
use FOS\JsRoutingBundle\Util\CacheControlConfig;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
/**
* Controller class.
*
* @author William DURAND <william.durand1@gmail.com>
*/
class Controller
{
protected CacheControlConfig $cacheControlConfig;
/**
* Default constructor.
*
* @param object $serializer Any object with a serialize($data, $format) method
* @param ExposedRoutesExtractorInterface $exposedRoutesExtractor the extractor service
* @param bool $debug
*/
public function __construct(
private RoutesResponse $routesResponse,
private mixed $serializer,
private ExposedRoutesExtractorInterface $exposedRoutesExtractor,
array $cacheControl = [],
private bool $debug = false,
) {
$this->cacheControlConfig = new CacheControlConfig($cacheControl);
}
public function indexAction(Request $request, $_format): Response
{
if (!$request->attributes->getBoolean('_stateless') && $request->hasSession()
&& ($session = $request->getSession())->isStarted() && $session->getFlashBag() instanceof AutoExpireFlashBag
) {
// keep current flashes for one more request if using AutoExpireFlashBag
$session->getFlashBag()->setAll($session->getFlashBag()->peekAll());
}
$cache = new ConfigCache($this->exposedRoutesExtractor->getCachePath($request->getLocale()), $this->debug);
if (!$cache->isFresh() || $this->debug) {
$exposedRoutes = $this->exposedRoutesExtractor->getRoutes();
$serializedRoutes = $this->serializer->serialize($exposedRoutes, 'json');
$cache->write($serializedRoutes, $this->exposedRoutesExtractor->getResources());
} else {
$path = method_exists($cache, 'getPath') ? $cache->getPath() : (string) $cache;
$serializedRoutes = file_get_contents($path);
$exposedRoutes = $this->serializer->deserialize(
$serializedRoutes,
'Symfony\Component\Routing\RouteCollection',
'json'
);
}
$this->routesResponse->setBaseUrl($this->exposedRoutesExtractor->getBaseUrl());
$this->routesResponse->setRoutes($exposedRoutes);
$this->routesResponse->setPrefix($this->exposedRoutesExtractor->getPrefix($request->getLocale()));
$this->routesResponse->setHost($this->exposedRoutesExtractor->getHost());
$this->routesResponse->setPort($this->exposedRoutesExtractor->getPort());
$this->routesResponse->setScheme($this->exposedRoutesExtractor->getScheme());
$this->routesResponse->setLocale($request->getLocale());
$this->routesResponse->setDomains($request->query->has('domain') ? explode(',', $request->query->get('domain')) : []);
$content = $this->serializer->serialize($this->routesResponse, 'json');
if (null !== $callback = $request->query->get('callback')) {
if (!\JsonpCallbackValidator::validate($callback)) {
throw new BadRequestHttpException('Invalid JSONP callback value');
}
$content = '/**/'.$callback.'('.$content.');';
}
$response = new Response($content, 200, ['Content-Type' => $request->getMimeType($_format)]);
$this->cacheControlConfig->apply($response);
return $response;
}
}

View File

@@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
/*
* This file is part of the FOSJsRoutingBundle package.
*
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\JsRoutingBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
* Configuration class.
*
* @author William DURAND <william.durand1@gmail.com>
*/
class Configuration implements ConfigurationInterface
{
/**
* Generates the configuration tree builder.
*
* @return TreeBuilder The tree builder
*/
public function getConfigTreeBuilder(): TreeBuilder
{
$builder = new TreeBuilder('fos_js_routing');
$rootNode = $builder->getRootNode();
$rootNode
->children()
->scalarNode('serializer')->cannotBeEmpty()->end()
->arrayNode('routes_to_expose')
->beforeNormalization()
->ifTrue(fn ($v) => !is_array($v))
->then(fn ($v) => [$v])
->end()
->prototype('scalar')->end()
->end()
->scalarNode('router')->defaultValue('router')->end()
->scalarNode('request_context_base_url')->defaultNull()->end()
->arrayNode('cache_control')
->children()
->booleanNode('public')->defaultFalse()->end()
->scalarNode('expires')->defaultNull()->end()
->scalarNode('maxage')->defaultNull()->end()
->scalarNode('smaxage')->defaultNull()->end()
->arrayNode('vary')
->beforeNormalization()
->ifTrue(fn ($v) => !is_array($v))
->then(fn ($v) => [$v])
->end()
->prototype('scalar')->end()
->end()
->end()
->end()
->end();
return $builder;
}
}

View File

@@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
/*
* This file is part of the FOSJsRoutingBundle package.
*
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\JsRoutingBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Processor;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
/**
* @author William DURAND <william.durand1@gmail.com>
*/
class FOSJsRoutingExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container): void
{
$processor = new Processor();
$configuration = new Configuration();
$config = $processor->processConfiguration($configuration, $configs);
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.xml');
$loader->load('controllers.xml');
if (isset($config['serializer'])) {
$container->setAlias('fos_js_routing.serializer', new Alias($config['serializer'], false));
} else {
$loader->load('serializer.xml');
}
$container->setAlias(
'fos_js_routing.router',
new Alias($config['router'], false)
);
$container
->getDefinition('fos_js_routing.extractor')
->replaceArgument(1, $config['routes_to_expose']);
$container->setParameter(
'fos_js_routing.request_context_base_url',
$config['request_context_base_url'] ?: null
);
if (isset($config['cache_control'])) {
$config['cache_control']['enabled'] = true;
} else {
$config['cache_control'] = ['enabled' => false];
}
$container->setParameter('fos_js_routing.cache_control', $config['cache_control']);
}
}

View File

@@ -0,0 +1,260 @@
<?php
declare(strict_types=1);
/*
* This file is part of the FOSJsRoutingBundle package.
*
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\JsRoutingBundle\Extractor;
use JMS\I18nRoutingBundle\Router\I18nLoader;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\RouterInterface;
/**
* @author William DURAND <william.durand1@gmail.com>
*/
class ExposedRoutesExtractor implements ExposedRoutesExtractorInterface
{
protected string $pattern;
protected array $availableDomains;
/**
* Default constructor.
*
* @param array $routesToExpose some route names to expose
* @param array $bundles list of loaded bundles to check when generating the prefix
*
* @throws \Exception
*/
public function __construct(private RouterInterface $router, array $routesToExpose, private string $cacheDir, private array $bundles = [])
{
$domainPatterns = $this->extractDomainPatterns($routesToExpose);
$this->availableDomains = array_keys($domainPatterns);
$this->pattern = $this->buildPattern($domainPatterns);
}
/**
* {@inheritDoc}
*/
public function getRoutes(): RouteCollection
{
$collection = $this->router->getRouteCollection();
$routes = new RouteCollection();
/** @var Route $route */
foreach ($collection->all() as $name => $route) {
if ($route->hasOption('expose')) {
$expose = $route->getOption('expose');
if (false !== $expose && 'false' !== $expose) {
$routes->add($name, $route);
}
continue;
}
preg_match('#^'.$this->pattern.'$#', $name, $matches);
if (0 === count($matches)) {
continue;
}
$domain = $this->getDomainByRouteMatches($matches, $name);
if (is_null($domain)) {
continue;
}
$route = clone $route;
$route->setOption('expose', $domain);
$routes->add($name, $route);
}
return $routes;
}
/**
* {@inheritDoc}
*/
public function getBaseUrl(): string
{
return $this->router->getContext()->getBaseUrl() ?: '';
}
/**
* {@inheritDoc}
*/
public function getPrefix(string $locale): string
{
if (isset($this->bundles['JMSI18nRoutingBundle'])) {
return $locale.I18nLoader::ROUTING_PREFIX;
}
return '';
}
/**
* {@inheritDoc}
*/
public function getHost(): string
{
$requestContext = $this->router->getContext();
$host = $requestContext->getHost().
('' === $this->getPort() ? $this->getPort() : ':'.$this->getPort());
return $host;
}
/**
* {@inheritDoc}
*/
public function getPort(): ?string
{
$requestContext = $this->router->getContext();
$port = '';
if ($this->usesNonStandardPort()) {
$method = sprintf('get%sPort', ucfirst($requestContext->getScheme()));
$port = (string) $requestContext->$method();
}
return $port;
}
/**
* {@inheritDoc}
*/
public function getScheme(): string
{
return $this->router->getContext()->getScheme();
}
/**
* {@inheritDoc}
*/
public function getCachePath(?string $locale = null): string
{
$cachePath = $this->cacheDir.DIRECTORY_SEPARATOR.'fosJsRouting';
if (!file_exists($cachePath)) {
if (false === @mkdir($cachePath)) {
throw new \RuntimeException('Unable to create Cache directory ' . $cachePath);
}
}
if (isset($this->bundles['JMSI18nRoutingBundle'])) {
$cachePath = $cachePath.DIRECTORY_SEPARATOR.'data.'.$locale.'.json';
} else {
$cachePath = $cachePath.DIRECTORY_SEPARATOR.'data.json';
}
return $cachePath;
}
/**
* {@inheritDoc}
*/
public function getResources(): array
{
return $this->router->getRouteCollection()->getResources();
}
/**
* {@inheritDoc}
*/
public function isRouteExposed(Route $route, $name): bool
{
if (false === $route->hasOption('expose')) {
return '' !== $this->pattern && preg_match('#^'.$this->pattern.'$#', $name);
}
$status = $route->getOption('expose');
return false !== $status && 'false' !== $status;
}
protected function getDomainByRouteMatches($matches, $name): int|string|null
{
$matches = array_filter($matches, fn ($match) => !empty($match));
$matches = array_flip(array_intersect_key($matches, array_flip($this->availableDomains)));
return $matches[$name] ?? null;
}
protected function extractDomainPatterns($routesToExpose): array
{
$domainPatterns = [];
foreach ($routesToExpose as $item) {
if (is_string($item)) {
$domainPatterns['default'][] = $item;
continue;
}
if (is_array($item) && is_string($item['pattern'])) {
if (!isset($item['domain'])) {
$domainPatterns['default'][] = $item['pattern'];
continue;
} elseif (is_string($item['domain'])) {
$domainPatterns[$item['domain']][] = $item['pattern'];
continue;
}
}
throw new \Exception('routes_to_expose definition is invalid');
}
return $domainPatterns;
}
/**
* Convert the routesToExpose array in a regular expression pattern.
*
* @throws \Exception
*/
protected function buildPattern(array $domainPatterns): string
{
$patterns = [];
foreach ($domainPatterns as $domain => $items) {
$patterns[] = '(?P<'.$domain.'>'.implode('|', $items).')';
}
return implode('|', $patterns);
}
/**
* Check whether server is serving this request from a non-standard port.
*/
private function usesNonStandardPort(): bool
{
return $this->usesNonStandardHttpPort() || $this->usesNonStandardHttpsPort();
}
/**
* Check whether server is serving HTTP over a non-standard port.
*/
private function usesNonStandardHttpPort(): bool
{
return 'http' === $this->getScheme() && '80' != $this->router->getContext()->getHttpPort();
}
/**
* Check whether server is serving HTTPS over a non-standard port.
*/
private function usesNonStandardHttpsPort(): bool
{
return 'https' === $this->getScheme() && '443' != $this->router->getContext()->getHttpsPort();
}
}

View File

@@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
/*
* This file is part of the FOSJsRoutingBundle package.
*
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\JsRoutingBundle\Extractor;
use Symfony\Component\Config\Resource\ResourceInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
/**
* @author William DURAND <william.durand1@gmail.com>
*/
interface ExposedRoutesExtractorInterface
{
/**
* Returns a collection of exposed routes.
*/
public function getRoutes(): RouteCollection;
/**
* Return the Base URL.
*/
public function getBaseUrl(): string;
/**
* Get the route prefix to use, i.e. the language if JMSI18nRoutingBundle is active.
*/
public function getPrefix(string $locale): string;
/**
* Get the host and applicable port from RequestContext.
*/
public function getHost(): string;
/**
* Get the port from RequestContext, only if non standard port (Eg: "8080").
*/
public function getPort(): ?string;
/**
* Get the scheme from RequestContext.
*/
public function getScheme(): string;
/**
* Get the cache path for this request.
*
* @param string|null $locale the request locale
*/
public function getCachePath(?string $locale): string;
/**
* Return an array of routing resources.
*
* @return ResourceInterface[]
*/
public function getResources(): array;
/**
* Tell whether a route should be considered as exposed.
*/
public function isRouteExposed(Route $route, string $name): bool;
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
/*
* This file is part of the FOSJsRoutingBundle package.
*
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\JsRoutingBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
/**
* FOSJsRoutingBundle class.
*
* @author William DURAND <william.durand1@gmail.com>
*/
class FOSJsRoutingBundle extends Bundle
{
}

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="fos_js_routing.controller.class">FOS\JsRoutingBundle\Controller\Controller</parameter>
</parameters>
<services>
<service id="fos_js_routing.controller" class="%fos_js_routing.controller.class%" public="true">
<argument type="service" id="fos_js_routing.routes_response" />
<argument type="service" id="fos_js_routing.serializer" />
<argument type="service" id="fos_js_routing.extractor" />
<argument>%fos_js_routing.cache_control%</argument>
<argument>%kernel.debug%</argument>
</service>
</services>
</container>

View File

@@ -0,0 +1,21 @@
{
"id": "router",
"paths": ["../../js"],
"mode": "ADVANCED",
"level": "VERBOSE",
"inputs": "../../js/export.js",
"externs": "../../js/externs.js",
"define": {
"goog.DEBUG": false
},
"type-prefixes-to-strip": ["goog.debug", "goog.asserts", "goog.assert", "console"],
"name-suffixes-to-strip": ["logger", "logger_"],
"output-file": "../../public/js/router.js",
"output-wrapper": "/**\n * Portions of this code are from the Google Closure Library,\n * received from the Closure Authors under the Apache 2.0 license.\n *\n * All other code is (C) FriendsOfSymfony and subject to the MIT license.\n */\n(function() {%output%})();",
"pretty-print": false,
"debug": false
}

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
<route id="fos_js_routing_js" path="/js/routing.{_format}" methods="GET">
<default key="_controller">fos_js_routing.controller::indexAction</default>
<default key="_format">js</default>
<requirement key="_format">js|json</requirement>
</route>
</routes>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="fos_js_routing.normalizer.route_collection.class">FOS\JsRoutingBundle\Serializer\Normalizer\RouteCollectionNormalizer</parameter>
<parameter key="fos_js_routing.normalizer.routes_response.class">FOS\JsRoutingBundle\Serializer\Normalizer\RoutesResponseNormalizer</parameter>
<parameter key="fos_js_routing.denormalizer.route_collection.class">FOS\JsRoutingBundle\Serializer\Denormalizer\RouteCollectionDenormalizer</parameter>
</parameters>
<services>
<service id="fos_js_routing.serializer" class="Symfony\Component\Serializer\Serializer" public="true">
<argument type="collection">
<argument type="service" id="fos_js_routing.normalizer.route_collection" />
<argument type="service" id="fos_js_routing.normalizer.routes_response" />
<argument type="service" id="fos_js_routing.denormalizer.route_collection" />
</argument>
<argument type="collection">
<argument key="json" type="service" id="fos_js_routing.encoder" />
</argument>
</service>
<service id="fos_js_routing.normalizer.route_collection" class="%fos_js_routing.normalizer.route_collection.class%" public="false" />
<service id="fos_js_routing.normalizer.routes_response" class="%fos_js_routing.normalizer.routes_response.class%" public="false" />
<service id="fos_js_routing.denormalizer.route_collection" class="%fos_js_routing.denormalizer.route_collection.class%" public="false" />
<service id="fos_js_routing.encoder" class="Symfony\Component\Serializer\Encoder\JsonEncoder" public="false" />
</services>
</container>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="fos_js_routing.extractor.class">FOS\JsRoutingBundle\Extractor\ExposedRoutesExtractor</parameter>
<parameter key="fos_js_routing.routes_response.class">FOS\JsRoutingBundle\Response\RoutesResponse</parameter>
</parameters>
<services>
<service id="fos_js_routing.extractor" class="%fos_js_routing.extractor.class%" public="true">
<argument type="service" id="fos_js_routing.router" />
<argument></argument>
<argument>%kernel.cache_dir%</argument>
<argument>%kernel.bundles%</argument>
</service>
<service id="fos_js_routing.routes_response" class="%fos_js_routing.routes_response.class%" public="true" />
<service id="fos_js_routing.dump_command" class="FOS\JsRoutingBundle\Command\DumpCommand">
<argument type="service" id="fos_js_routing.routes_response" />
<argument type="service" id="fos_js_routing.extractor" />
<argument type="service" id="fos_js_routing.serializer" />
<argument>%kernel.project_dir%</argument>
<argument>%fos_js_routing.request_context_base_url%</argument>
<tag name="console.command" />
</service>
<service id="fos_js_routing.router_debug_exposed_command" class="FOS\JsRoutingBundle\Command\RouterDebugExposedCommand">
<argument type="service" id="fos_js_routing.extractor" />
<argument type="service" id="router" />
<tag name="console.command" />
</service>
</services>
</container>

View File

@@ -0,0 +1,24 @@
const gulp = require('gulp');
const rename = require('gulp-rename');
const uglify = require('gulp-uglify');
const wrap = require('gulp-wrap');
const ts = require('gulp-typescript')
gulp.task('ts', function() {
return gulp.src('js/router.ts')
.pipe(ts({
noImplicitAny: true,
}))
.pipe(gulp.dest('public/js'));
});
gulp.task('min', function() {
return gulp.src('public/js/!(*.min).js')
.pipe(wrap({ src: 'js/router.template.js' }))
.pipe(gulp.dest('public/js'))
.pipe(rename({ extname: '.min.js' }))
.pipe(uglify())
.pipe(gulp.dest('public/js'));
});
gulp.task('default', gulp.series('ts', 'min'));

View File

@@ -0,0 +1,23 @@
(function (root, factory) {
var routing = factory();
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([], routing.Routing);
} else if (typeof module === 'object' && module.exports) {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = routing.Routing;
} else {
// Browser globals (root is window)
root.Routing = routing.Routing;
root.fos = {
Router: routing.Router
};
}
}(this, function () {
var exports = {};
<%= contents %>
return { Router: exports.Router, Routing: exports.Routing };
}));

View File

@@ -0,0 +1,353 @@
export interface RouteDefaults {
[index: string]: string | null;
}
export interface RouteRequirements {
[index: string]: string;
}
export interface RouteParams {
[index: string]: any;
}
export interface QueryParamAddFunction {
(prefix: string, params: any): void;
}
export interface Route {
tokens: (string|boolean)[][];
defaults: undefined[] | RouteDefaults;
requirements: undefined[] | RouteRequirements;
hosttokens: string[][];
schemes: string[];
methods: string[];
}
export interface RoutesMap {
[index: string]: Route;
}
export interface Context {
base_url: string;
prefix: string;
host: string;
port: string | null;
scheme: string;
locale: string | null;
}
export interface RoutingData {
base_url: string;
routes: RoutesMap;
prefix?: string;
host: string;
port?: string | null;
scheme?: string;
locale?: string | null;
}
export class Router {
private context_: Context;
private routes_!: RoutesMap;
static getInstance(): Router {
return Routing;
}
static setData(data: RoutingData): void {
const router = Router.getInstance();
router.setRoutingData(data);
}
constructor(context?: Context, routes?: RoutesMap) {
this.context_ = context || { base_url: '', prefix: '', host: '', port: '', scheme: '', locale: '' };
this.setRoutes(routes || {});
}
setRoutingData(data: RoutingData): void {
this.setBaseUrl(data['base_url']);
this.setRoutes(data['routes']);
if (typeof data.prefix !== 'undefined') {
this.setPrefix(data['prefix']);
}
if (typeof data.port !== 'undefined') {
this.setPort(data['port']);
}
if (typeof data.locale !== 'undefined') {
this.setLocale(data['locale']);
}
this.setHost(data['host']);
if (typeof data.scheme !== 'undefined') {
this.setScheme(data['scheme']);
}
}
setRoutes(routes: RoutesMap): void {
this.routes_ = Object.freeze(routes);
}
getRoutes(): RoutesMap {
return this.routes_;
}
setBaseUrl(baseUrl: string): void {
this.context_.base_url = baseUrl;
}
getBaseUrl(): string {
return this.context_.base_url;
}
setPrefix(prefix: string): void {
this.context_.prefix = prefix;
}
setScheme(scheme: string): void {
this.context_.scheme = scheme;
}
getScheme(): string {
return this.context_.scheme;
}
setHost(host: string): void {
this.context_.host = host;
}
getHost(): string {
return this.context_.host;
}
setPort(port: string | null) {
this.context_.port = port;
}
getPort(): string | null {
return this.context_.port;
};
setLocale(locale: string | null) {
this.context_.locale = locale;
}
getLocale(): string | null {
return this.context_.locale;
};
/**
* Builds query string params added to a URL.
* Port of jQuery's $.param() function, so credit is due there.
*/
buildQueryParams(prefix: string, params: any, add: QueryParamAddFunction): void {
let name;
let rbracket = new RegExp(/\[\]$/);
if (params instanceof Array) {
params.forEach((val, i) => {
if (rbracket.test(prefix)) {
add(prefix, val);
} else {
this.buildQueryParams(prefix + '[' + (typeof val === 'object' ? i : '') + ']', val, add);
}
});
} else if (typeof params === 'object') {
for (name in params) {
this.buildQueryParams(prefix + '[' + name + ']', params[name], add);
}
} else {
add(prefix, params);
}
}
/**
* Returns a raw route object.
*/
getRoute(name: string): Route {
let prefixedName = this.context_.prefix + name;
let sf41i18nName = name + '.' + this.context_.locale;
let prefixedSf41i18nName = this.context_.prefix + name + '.' + this.context_.locale;
let variants = [prefixedName, sf41i18nName, prefixedSf41i18nName, name];
for (let i in variants) {
if (variants[i] in this.routes_) {
return this.routes_[variants[i]];
}
}
throw new Error('The route "' + name + '" does not exist.');
}
/**
* Generates the URL for a route.
*/
generate(name: string, opt_params?: RouteParams, absolute?: boolean): string {
let route = (this.getRoute(name));
let params = opt_params || {};
let unusedParams = Object.assign({}, params);
let url = '';
let optional = true;
let host = '';
let port = (typeof this.getPort() == 'undefined' || this.getPort() === null) ? '' : this.getPort();
route.tokens.forEach((token) => {
if ('text' === token[0] && typeof token[1] === 'string') {
url = Router.encodePathComponent(token[1]) + url;
optional = false;
return;
}
if ('variable' === token[0]) {
if (token.length === 6 && token[5] === true) { // Sixth part of the token array indicates if it should be included in case of defaults
optional = false;
}
let hasDefault = route.defaults && !Array.isArray(route.defaults) && typeof token[3] === 'string' && (token[3] in route.defaults);
if (false === optional || !hasDefault || ((typeof token[3] === 'string' && token[3] in params) && !Array.isArray(route.defaults) && params[token[3]] != route.defaults[token[3]])) {
let value;
if (typeof token[3] === 'string' && token[3] in params) {
value = params[token[3]];
delete unusedParams[token[3]];
} else if (typeof token[3] === 'string' && hasDefault && !Array.isArray(route.defaults)) {
value = route.defaults[token[3]];
} else if (optional) {
return;
} else {
throw new Error('The route "' + name + '" requires the parameter "' + token[3] + '".');
}
let empty = true === value || false === value || '' === value;
if (!empty || !optional) {
let encodedValue = Router.encodePathComponent(value);
if ('null' === encodedValue && null === value) {
encodedValue = '';
}
url = token[1] + encodedValue + url;
}
optional = false;
} else if (hasDefault && (typeof token[3] === 'string' && token[3] in unusedParams)) {
delete unusedParams[token[3]];
}
return;
}
throw new Error('The token type "' + token[0] + '" is not supported.');
});
if (url === '') {
url = '/';
}
route.hosttokens.forEach((token) => {
let value;
if ('text' === token[0]) {
host = token[1] + host;
return;
}
if ('variable' === token[0]) {
if (token[3] in params) {
value = params[token[3]];
delete unusedParams[token[3]];
} else if (route.defaults && !Array.isArray(route.defaults) && (token[3] in route.defaults)) {
value = route.defaults[token[3]];
}
host = token[1] + value + host;
}
});
url = this.context_.base_url + url;
if (route.requirements && ('_scheme' in route.requirements) && this.getScheme() != route.requirements['_scheme']) {
const currentHost = host || this.getHost();
url = route.requirements['_scheme'] + '://' + currentHost + (currentHost.indexOf(':' + port) > -1 || '' === port ? '' : ':' + port) + url;
} else if ('undefined' !== typeof route.schemes && 'undefined' !== typeof route.schemes[0] && this.getScheme() !== route.schemes[0]) {
const currentHost = host || this.getHost();
url = route.schemes[0] + '://' + currentHost + (currentHost.indexOf(':' + port) > -1 || '' === port ? '' : ':' + port) + url;
} else if (host && this.getHost() !== host + (host.indexOf(':' + port) > -1 || '' === port ? '' : ':' + port)) {
url = this.getScheme() + '://' + host + (host.indexOf(':' + port) > -1 || '' === port ? '' : ':' + port) + url;
} else if (absolute === true) {
url = this.getScheme() + '://' + this.getHost() + (this.getHost().indexOf(':' + port) > -1 || '' === port ? '' : ':' + port) + url;
}
if (Object.keys(unusedParams).length > 0) {
let queryParams: string[] = [];
let add = (key: string, value: string|(() => string)) => {
// if value is a function then call it and assign it's return value as value
value = (typeof value === 'function') ? value() : value;
// change null to empty string
value = (value === null) ? '' : value;
queryParams.push(Router.encodeQueryComponent(key) + '=' + Router.encodeQueryComponent(value));
};
for (const prefix in unusedParams) {
if(unusedParams.hasOwnProperty(prefix)) {
this.buildQueryParams(prefix, unusedParams[prefix], add);
}
}
url = url + '?' + queryParams.join('&');
}
return url;
}
/**
* Returns the given string encoded to mimic Symfony URL generator.
*/
static customEncodeURIComponent(value: string): string {
return encodeURIComponent(value)
.replace(/%2F/g, '/')
.replace(/%40/g, '@')
.replace(/%3A/g, ':')
.replace(/%21/g, '!')
.replace(/%3B/g, ';')
.replace(/%2C/g, ',')
.replace(/%2A/g, '*')
.replace(/\(/g, '%28')
.replace(/\)/g, '%29')
.replace(/'/g, '%27')
;
}
/**
* Returns the given path properly encoded to mimic Symfony URL generator.
*/
static encodePathComponent(value: string): string {
return Router.customEncodeURIComponent(value)
.replace(/%3D/g, '=')
.replace(/%2B/g, '+')
.replace(/%21/g, '!')
.replace(/%7C/g, '|')
;
}
/**
* Returns the given query parameter or value properly encoded to mimic Symfony URL generator.
*/
static encodeQueryComponent(value: string): string {
return Router.customEncodeURIComponent(value)
.replace(/%3F/g, '?')
;
}
}
export const Routing = new Router();
export default Routing;

View File

@@ -0,0 +1,19 @@
Copyright (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,283 @@
(function (root, factory) {
var routing = factory();
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([], routing.Routing);
} else if (typeof module === 'object' && module.exports) {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = routing.Routing;
} else {
// Browser globals (root is window)
root.Routing = routing.Routing;
root.fos = {
Router: routing.Router
};
}
}(this, function () {
var exports = {};
"use strict";
exports.__esModule = true;
exports.Routing = exports.Router = void 0;
var Router = /** @class */ (function () {
function Router(context, routes) {
this.context_ = context || { base_url: '', prefix: '', host: '', port: '', scheme: '', locale: '' };
this.setRoutes(routes || {});
}
Router.getInstance = function () {
return exports.Routing;
};
Router.setData = function (data) {
var router = Router.getInstance();
router.setRoutingData(data);
};
Router.prototype.setRoutingData = function (data) {
this.setBaseUrl(data['base_url']);
this.setRoutes(data['routes']);
if (typeof data.prefix !== 'undefined') {
this.setPrefix(data['prefix']);
}
if (typeof data.port !== 'undefined') {
this.setPort(data['port']);
}
if (typeof data.locale !== 'undefined') {
this.setLocale(data['locale']);
}
this.setHost(data['host']);
if (typeof data.scheme !== 'undefined') {
this.setScheme(data['scheme']);
}
};
Router.prototype.setRoutes = function (routes) {
this.routes_ = Object.freeze(routes);
};
Router.prototype.getRoutes = function () {
return this.routes_;
};
Router.prototype.setBaseUrl = function (baseUrl) {
this.context_.base_url = baseUrl;
};
Router.prototype.getBaseUrl = function () {
return this.context_.base_url;
};
Router.prototype.setPrefix = function (prefix) {
this.context_.prefix = prefix;
};
Router.prototype.setScheme = function (scheme) {
this.context_.scheme = scheme;
};
Router.prototype.getScheme = function () {
return this.context_.scheme;
};
Router.prototype.setHost = function (host) {
this.context_.host = host;
};
Router.prototype.getHost = function () {
return this.context_.host;
};
Router.prototype.setPort = function (port) {
this.context_.port = port;
};
Router.prototype.getPort = function () {
return this.context_.port;
};
;
Router.prototype.setLocale = function (locale) {
this.context_.locale = locale;
};
Router.prototype.getLocale = function () {
return this.context_.locale;
};
;
/**
* Builds query string params added to a URL.
* Port of jQuery's $.param() function, so credit is due there.
*/
Router.prototype.buildQueryParams = function (prefix, params, add) {
var _this = this;
var name;
var rbracket = new RegExp(/\[\]$/);
if (params instanceof Array) {
params.forEach(function (val, i) {
if (rbracket.test(prefix)) {
add(prefix, val);
}
else {
_this.buildQueryParams(prefix + '[' + (typeof val === 'object' ? i : '') + ']', val, add);
}
});
}
else if (typeof params === 'object') {
for (name in params) {
this.buildQueryParams(prefix + '[' + name + ']', params[name], add);
}
}
else {
add(prefix, params);
}
};
/**
* Returns a raw route object.
*/
Router.prototype.getRoute = function (name) {
var prefixedName = this.context_.prefix + name;
var sf41i18nName = name + '.' + this.context_.locale;
var prefixedSf41i18nName = this.context_.prefix + name + '.' + this.context_.locale;
var variants = [prefixedName, sf41i18nName, prefixedSf41i18nName, name];
for (var i in variants) {
if (variants[i] in this.routes_) {
return this.routes_[variants[i]];
}
}
throw new Error('The route "' + name + '" does not exist.');
};
/**
* Generates the URL for a route.
*/
Router.prototype.generate = function (name, opt_params, absolute) {
var route = (this.getRoute(name));
var params = opt_params || {};
var unusedParams = Object.assign({}, params);
var url = '';
var optional = true;
var host = '';
var port = (typeof this.getPort() == 'undefined' || this.getPort() === null) ? '' : this.getPort();
route.tokens.forEach(function (token) {
if ('text' === token[0] && typeof token[1] === 'string') {
url = Router.encodePathComponent(token[1]) + url;
optional = false;
return;
}
if ('variable' === token[0]) {
if (token.length === 6 && token[5] === true) { // Sixth part of the token array indicates if it should be included in case of defaults
optional = false;
}
var hasDefault = route.defaults && !Array.isArray(route.defaults) && typeof token[3] === 'string' && (token[3] in route.defaults);
if (false === optional || !hasDefault || ((typeof token[3] === 'string' && token[3] in params) && !Array.isArray(route.defaults) && params[token[3]] != route.defaults[token[3]])) {
var value = void 0;
if (typeof token[3] === 'string' && token[3] in params) {
value = params[token[3]];
delete unusedParams[token[3]];
}
else if (typeof token[3] === 'string' && hasDefault && !Array.isArray(route.defaults)) {
value = route.defaults[token[3]];
}
else if (optional) {
return;
}
else {
throw new Error('The route "' + name + '" requires the parameter "' + token[3] + '".');
}
var empty = true === value || false === value || '' === value;
if (!empty || !optional) {
var encodedValue = Router.encodePathComponent(value);
if ('null' === encodedValue && null === value) {
encodedValue = '';
}
url = token[1] + encodedValue + url;
}
optional = false;
}
else if (hasDefault && (typeof token[3] === 'string' && token[3] in unusedParams)) {
delete unusedParams[token[3]];
}
return;
}
throw new Error('The token type "' + token[0] + '" is not supported.');
});
if (url === '') {
url = '/';
}
route.hosttokens.forEach(function (token) {
var value;
if ('text' === token[0]) {
host = token[1] + host;
return;
}
if ('variable' === token[0]) {
if (token[3] in params) {
value = params[token[3]];
delete unusedParams[token[3]];
}
else if (route.defaults && !Array.isArray(route.defaults) && (token[3] in route.defaults)) {
value = route.defaults[token[3]];
}
host = token[1] + value + host;
}
});
url = this.context_.base_url + url;
if (route.requirements && ('_scheme' in route.requirements) && this.getScheme() != route.requirements['_scheme']) {
var currentHost = host || this.getHost();
url = route.requirements['_scheme'] + '://' + currentHost + (currentHost.indexOf(':' + port) > -1 || '' === port ? '' : ':' + port) + url;
}
else if ('undefined' !== typeof route.schemes && 'undefined' !== typeof route.schemes[0] && this.getScheme() !== route.schemes[0]) {
var currentHost = host || this.getHost();
url = route.schemes[0] + '://' + currentHost + (currentHost.indexOf(':' + port) > -1 || '' === port ? '' : ':' + port) + url;
}
else if (host && this.getHost() !== host + (host.indexOf(':' + port) > -1 || '' === port ? '' : ':' + port)) {
url = this.getScheme() + '://' + host + (host.indexOf(':' + port) > -1 || '' === port ? '' : ':' + port) + url;
}
else if (absolute === true) {
url = this.getScheme() + '://' + this.getHost() + (this.getHost().indexOf(':' + port) > -1 || '' === port ? '' : ':' + port) + url;
}
if (Object.keys(unusedParams).length > 0) {
var queryParams_1 = [];
var add = function (key, value) {
// if value is a function then call it and assign it's return value as value
value = (typeof value === 'function') ? value() : value;
// change null to empty string
value = (value === null) ? '' : value;
queryParams_1.push(Router.encodeQueryComponent(key) + '=' + Router.encodeQueryComponent(value));
};
for (var prefix in unusedParams) {
if (unusedParams.hasOwnProperty(prefix)) {
this.buildQueryParams(prefix, unusedParams[prefix], add);
}
}
url = url + '?' + queryParams_1.join('&');
}
return url;
};
/**
* Returns the given string encoded to mimic Symfony URL generator.
*/
Router.customEncodeURIComponent = function (value) {
return encodeURIComponent(value)
.replace(/%2F/g, '/')
.replace(/%40/g, '@')
.replace(/%3A/g, ':')
.replace(/%21/g, '!')
.replace(/%3B/g, ';')
.replace(/%2C/g, ',')
.replace(/%2A/g, '*')
.replace(/\(/g, '%28')
.replace(/\)/g, '%29')
.replace(/'/g, '%27');
};
/**
* Returns the given path properly encoded to mimic Symfony URL generator.
*/
Router.encodePathComponent = function (value) {
return Router.customEncodeURIComponent(value)
.replace(/%3D/g, '=')
.replace(/%2B/g, '+')
.replace(/%21/g, '!')
.replace(/%7C/g, '|');
};
/**
* Returns the given query parameter or value properly encoded to mimic Symfony URL generator.
*/
Router.encodeQueryComponent = function (value) {
return Router.customEncodeURIComponent(value)
.replace(/%3F/g, '?');
};
return Router;
}());
exports.Router = Router;
exports.Routing = new Router();
exports["default"] = exports.Routing;
return { Router: exports.Router, Routing: exports.Routing };
}));

View File

@@ -0,0 +1 @@
!function(e){(t={}).__esModule=!0,t.Routing=t.Router=void 0,o=function(){function l(e,t){this.context_=e||{base_url:"",prefix:"",host:"",port:"",scheme:"",locale:""},this.setRoutes(t||{})}return l.getInstance=function(){return t.Routing},l.setData=function(e){l.getInstance().setRoutingData(e)},l.prototype.setRoutingData=function(e){this.setBaseUrl(e.base_url),this.setRoutes(e.routes),void 0!==e.prefix&&this.setPrefix(e.prefix),void 0!==e.port&&this.setPort(e.port),void 0!==e.locale&&this.setLocale(e.locale),this.setHost(e.host),void 0!==e.scheme&&this.setScheme(e.scheme)},l.prototype.setRoutes=function(e){this.routes_=Object.freeze(e)},l.prototype.getRoutes=function(){return this.routes_},l.prototype.setBaseUrl=function(e){this.context_.base_url=e},l.prototype.getBaseUrl=function(){return this.context_.base_url},l.prototype.setPrefix=function(e){this.context_.prefix=e},l.prototype.setScheme=function(e){this.context_.scheme=e},l.prototype.getScheme=function(){return this.context_.scheme},l.prototype.setHost=function(e){this.context_.host=e},l.prototype.getHost=function(){return this.context_.host},l.prototype.setPort=function(e){this.context_.port=e},l.prototype.getPort=function(){return this.context_.port},l.prototype.setLocale=function(e){this.context_.locale=e},l.prototype.getLocale=function(){return this.context_.locale},l.prototype.buildQueryParams=function(o,e,n){var t,r=this,s=new RegExp(/\[\]$/);if(e instanceof Array)e.forEach(function(e,t){s.test(o)?n(o,e):r.buildQueryParams(o+"["+("object"==typeof e?t:"")+"]",e,n)});else if("object"==typeof e)for(t in e)this.buildQueryParams(o+"["+t+"]",e[t],n);else n(o,e)},l.prototype.getRoute=function(e){var t,o=[this.context_.prefix+e,e+"."+this.context_.locale,this.context_.prefix+e+"."+this.context_.locale,e];for(t in o)if(o[t]in this.routes_)return this.routes_[o[t]];throw new Error('The route "'+e+'" does not exist.')},l.prototype.generate=function(r,e,p){var t,s=this.getRoute(r),i=e||{},u=Object.assign({},i),c="",a=!0,o="",e=void 0===this.getPort()||null===this.getPort()?"":this.getPort();if(s.tokens.forEach(function(e){if("text"===e[0]&&"string"==typeof e[1])return c=l.encodePathComponent(e[1])+c,void(a=!1);if("variable"!==e[0])throw new Error('The token type "'+e[0]+'" is not supported.');6===e.length&&!0===e[5]&&(a=!1);var t=s.defaults&&!Array.isArray(s.defaults)&&"string"==typeof e[3]&&e[3]in s.defaults;if(!1===a||!t||"string"==typeof e[3]&&e[3]in i&&!Array.isArray(s.defaults)&&i[e[3]]!=s.defaults[e[3]]){var o,n=void 0;if("string"==typeof e[3]&&e[3]in i)n=i[e[3]],delete u[e[3]];else{if("string"!=typeof e[3]||!t||Array.isArray(s.defaults)){if(a)return;throw new Error('The route "'+r+'" requires the parameter "'+e[3]+'".')}n=s.defaults[e[3]]}(!0===n||!1===n||""===n)&&a||(o=l.encodePathComponent(n),c=e[1]+(o="null"===o&&null===n?"":o)+c),a=!1}else t&&"string"==typeof e[3]&&e[3]in u&&delete u[e[3]]}),""===c&&(c="/"),s.hosttokens.forEach(function(e){var t;"text"!==e[0]?"variable"===e[0]&&(e[3]in i?(t=i[e[3]],delete u[e[3]]):s.defaults&&!Array.isArray(s.defaults)&&e[3]in s.defaults&&(t=s.defaults[e[3]]),o=e[1]+t+o):o=e[1]+o}),c=this.context_.base_url+c,s.requirements&&"_scheme"in s.requirements&&this.getScheme()!=s.requirements._scheme?(t=o||this.getHost(),c=s.requirements._scheme+"://"+t+(-1<t.indexOf(":"+e)||""===e?"":":"+e)+c):void 0!==s.schemes&&void 0!==s.schemes[0]&&this.getScheme()!==s.schemes[0]?(t=o||this.getHost(),c=s.schemes[0]+"://"+t+(-1<t.indexOf(":"+e)||""===e?"":":"+e)+c):o&&this.getHost()!==o+(-1<o.indexOf(":"+e)||""===e?"":":"+e)?c=this.getScheme()+"://"+o+(-1<o.indexOf(":"+e)||""===e?"":":"+e)+c:!0===p&&(c=this.getScheme()+"://"+this.getHost()+(-1<this.getHost().indexOf(":"+e)||""===e?"":":"+e)+c),0<Object.keys(u).length){function f(e,t){t=null===(t="function"==typeof t?t():t)?"":t,h.push(l.encodeQueryComponent(e)+"="+l.encodeQueryComponent(t))}var n,h=[];for(n in u)u.hasOwnProperty(n)&&this.buildQueryParams(n,u[n],f);c=c+"?"+h.join("&")}return c},l.customEncodeURIComponent=function(e){return encodeURIComponent(e).replace(/%2F/g,"/").replace(/%40/g,"@").replace(/%3A/g,":").replace(/%21/g,"!").replace(/%3B/g,";").replace(/%2C/g,",").replace(/%2A/g,"*").replace(/\(/g,"%28").replace(/\)/g,"%29").replace(/'/g,"%27")},l.encodePathComponent=function(e){return l.customEncodeURIComponent(e).replace(/%3D/g,"=").replace(/%2B/g,"+").replace(/%21/g,"!").replace(/%7C/g,"|")},l.encodeQueryComponent=function(e){return l.customEncodeURIComponent(e).replace(/%3F/g,"?")},l}(),t.Router=o,t.Routing=new o,t.default=t.Routing;var t,o={Router:t.Router,Routing:t.Routing};"function"==typeof define&&define.amd?define([],o.Routing):"object"==typeof module&&module.exports?module.exports=o.Routing:(e.Routing=o.Routing,e.fos={Router:o.Router})}(this);

View File

@@ -0,0 +1,88 @@
export interface RouteDefaults {
[index: string]: string | null;
}
export interface RouteRequirements {
[index: string]: string;
}
export interface RouteParams {
[index: string]: any;
}
export interface QueryParamAddFunction {
(prefix: string, params: any): void;
}
export interface Route {
tokens: (string | boolean)[][];
defaults: undefined[] | RouteDefaults;
requirements: undefined[] | RouteRequirements;
hosttokens: string[][];
schemes: string[];
methods: string[];
}
export interface RoutesMap {
[index: string]: Route;
}
export interface Context {
base_url: string;
prefix: string;
host: string;
port: string | null;
scheme: string;
locale: string | null;
}
export interface RoutingData {
base_url: string;
routes: RoutesMap;
prefix?: string;
host: string;
port?: string | null;
scheme?: string;
locale?: string | null;
}
export declare class Router {
private context_;
private routes_;
static getInstance(): Router;
static setData(data: RoutingData): void;
constructor(context?: Context, routes?: RoutesMap);
setRoutingData(data: RoutingData): void;
setRoutes(routes: RoutesMap): void;
getRoutes(): RoutesMap;
setBaseUrl(baseUrl: string): void;
getBaseUrl(): string;
setPrefix(prefix: string): void;
setScheme(scheme: string): void;
getScheme(): string;
setHost(host: string): void;
getHost(): string;
setPort(port: string | null): void;
getPort(): string | null;
setLocale(locale: string | null): void;
getLocale(): string | null;
/**
* Builds query string params added to a URL.
* Port of jQuery's $.param() function, so credit is due there.
*/
buildQueryParams(prefix: string, params: any, add: QueryParamAddFunction): void;
/**
* Returns a raw route object.
*/
getRoute(name: string): Route;
/**
* Generates the URL for a route.
*/
generate(name: string, opt_params?: RouteParams, absolute?: boolean): string;
/**
* Returns the given string encoded to mimic Symfony URL generator.
*/
static customEncodeURIComponent(value: string): string;
/**
* Returns the given path properly encoded to mimic Symfony URL generator.
*/
static encodePathComponent(value: string): string;
/**
* Returns the given query parameter or value properly encoded to mimic Symfony URL generator.
*/
static encodeQueryComponent(value: string): string;
}
export declare const Routing: Router;
export default Routing;

View File

@@ -0,0 +1,89 @@
import { expectType } from 'tsd';
import type { RoutesMap } from '../js/router';
import { type Route, Router, Routing } from './router';
import routes from './routes.json';
expectType<Router>(Router.getInstance());
expectType<Router>(Routing);
expectType<RoutesMap>(Routing.getRoutes());
expectType<Route>(Routing.getRoute('homepage'));
expectType<string>(Routing.getBaseUrl());
Routing.setBaseUrl('');
expectType<string>(Routing.getScheme());
Routing.setScheme('https');
expectType<string>(Routing.getHost());
Routing.setHost('localhost');
expectType<string | null>(Routing.getPort());
Routing.setPort('1234');
expectType<string | null>(Routing.getLocale());
Routing.setLocale('en');
Routing.setRoutingData(routes);
Routing.setRoutingData({
base_url: '',
routes: {
homepage: { tokens: [['text', '/']], defaults: [], requirements: [], hosttokens: [], methods: [], schemes: [], },
admin_index: { tokens: [['text', '/admin']], defaults: [], requirements: [], hosttokens: [], methods: [], schemes: [], },
admin_pages: { tokens: [['text', '/admin/path']], defaults: [], requirements: [], hosttokens: [], methods: [], schemes: [], },
blog_index: { tokens: [['text', '/blog']], defaults: [], requirements: [], hosttokens: [['text', 'localhost']], methods: [], schemes: [], },
blog_post: {
tokens: [
['variable', '/', '[^/]++', 'slug'],
['text', '/blog'],
],
defaults: [],
requirements: [],
hosttokens: [['text', 'localhost']],
methods: [],
schemes: [],
},
users_delete: {
tokens: [
['text', '/delete'],
['variable', '/', '[^/]++', 'id', true],
['text', '/users']
],
defaults: [],
requirements: [],
hosttokens: [],
methods: [
'DELETE'
],
schemes: []
},
feed_post: {
tokens: [
['variable', '.', 'js|json', '_format', true],
['text', '/feed/post']
],
defaults: {
_format: 'xml',
},
requirements: {
_format: 'xml|json',
},
hosttokens: [],
methods: ['GET'],
schemes: [],
},
},
prefix: '',
host: '',
port: null,
scheme: '',
locale: 'en',
});
expectType<string>(Routing.generate('homepage'));
expectType<string>(Routing.generate('blog_post', {
slug: 'my-blog-post',
}));
expectType<string>(Routing.generate('users_delete', {
id: 123,
}));

View File

@@ -0,0 +1,33 @@
{
"base_url": "",
"routes": {
"feed_post": {
"tokens": [
[
"variable",
".",
"js|json",
"_format",
true
],
["text", "/feed/post"]
],
"defaults": {
"_format": "xml"
},
"requirements": {
"_format": "xml|json"
},
"hosttokens": [],
"methods": [
"GET"
],
"schemes": []
}
},
"prefix": "",
"host": "localhost",
"port": "",
"scheme": "https",
"locale": null
}

View File

@@ -0,0 +1,8 @@
{
"compilerOptions": {
"declaration": true,
"declarationDir": "ts",
"resolveJsonModule": true,
"esModuleInterop": true
}
}

View File

@@ -0,0 +1,106 @@
/**
* @author Adrien Foulon <tofandel@tukan.hu>
*/
const fs = require('fs');
const path = require('path');
const util = require('util');
const InjectPlugin = require('@bpnetguy/webpack-inject-plugin').default;
const execFile = util.promisify(require('child_process').execFile);
const readFile = util.promisify(fs.readFile);
const rmFile = util.promisify(fs.rm);
const writeFile = util.promisify(fs.writeFile);
const makeDir = util.promisify(fs.mkdir)
class FosRouting {
default = {
locale: '',
prettyPrint: false,
domain: [],
php: 'php'
};
constructor(options = {}, registerCompileHooks = true) {
this.options = Object.assign({target: 'var/cache/fosRoutes.json'}, this.default, options, {format: 'json'});
this.finalTarget = path.resolve(process.cwd(), this.options.target);
this.options.target = path.resolve(process.cwd(), this.options.target.replace(/\.json$/, '.tmp.json'));
if (this.options.target === this.finalTarget) {
this.options.target += '.tmp';
}
this.registerCompileHooks = registerCompileHooks;
}
// Values don't need to be escaped because node already does that
shellArg(key, value) {
key = this.kebabize(key);
return typeof value === 'boolean' ? (value ? '--' + key : '') : '--' + key + '=' + value;
}
kebabize(str) {
return str.split('').map((letter, idx) => {
return letter.toUpperCase() === letter
? `${idx !== 0 ? '-' : ''}${letter.toLowerCase()}`
: letter;
}).join('');
}
apply(compiler) {
let prevContent = null;
try {
fs.readFileSync(this.finalTarget);
} catch (e) {
}
const compile = async (comp, callback) => {
const args = Object.keys(this.options).reduce((pass, key) => {
const val = this.options[key];
if (val !== this.default[key]) {
if (Array.isArray(val)) {
pass.push(...val.map((v) => this.shellArg(key, v)));
} else {
pass.push(this.shellArg(key, val));
}
}
return pass;
}, []);
await execFile(this.options.php, ['bin/console', 'fos:js-routing:dump', ...args]);
try {
const content = await readFile(this.options.target);
await rmFile(this.options.target);
if (!prevContent || content.compare(prevContent) !== 0) {
await makeDir(path.dirname(this.finalTarget), {recursive: true});
await writeFile(this.finalTarget, content);
prevContent = content;
if (comp.modifiedFiles && !comp.modifiedFiles.has(this.finalTarget)) {
comp.modifiedFiles.add(this.finalTarget);
}
}
} catch (e) {
const logger = compiler.getInfrastructureLogger('FosRouting');
logger.error(e.toString());
}
callback();
};
if (this.registerCompileHooks === true) {
compiler.hooks.beforeRun.tapAsync('RouteDump', compile);
compiler.hooks.watchRun.tapAsync('RouteDump_Watch', (comp, callback) => {
if (!comp.modifiedFiles || !comp.modifiedFiles.has(this.finalTarget)) {
compile(comp, callback);
} else {
callback();
}
});
}
new InjectPlugin(() => {
return 'import Routing from "fos-router";' +
'import routes from '+JSON.stringify(this.finalTarget)+';' +
'Routing.setRoutingData(routes);';
}).apply(compiler);
}
}
module.exports = FosRouting;
module.exports.default = FosRouting;

View File

@@ -0,0 +1,152 @@
<?php
declare(strict_types=1);
/*
* This file is part of the FOSJsRoutingBundle package.
*
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\JsRoutingBundle\Response;
use Symfony\Component\Routing\RouteCollection;
class RoutesResponse
{
protected $routes;
public function __construct(
protected ?string $baseUrl = null,
?RouteCollection $routes = null,
protected ?string $prefix = null,
protected ?string $host = null,
protected ?string $port = null,
protected ?string $scheme = null,
protected ?string $locale = null,
protected array $domains = [],
) {
$this->routes = $routes ?: new RouteCollection();
}
public function getRoutes(): array
{
$exposedRoutes = [];
foreach ($this->routes->all() as $name => $route) {
if (!$route->hasOption('expose')) {
$domain = 'default';
} else {
$domain = $route->getOption('expose');
$domain = is_string($domain) ? ('true' === $domain ? 'default' : $domain) : 'default';
}
if (0 === count($this->domains)) {
if ('default' !== $domain) {
continue;
}
} elseif (!in_array($domain, $this->domains, true)) {
continue;
}
$compiledRoute = $route->compile();
$defaults = array_intersect_key(
$route->getDefaults(),
array_fill_keys($compiledRoute->getVariables(), null)
);
if (!isset($defaults['_locale']) && in_array('_locale', $compiledRoute->getVariables())) {
$defaults['_locale'] = $this->locale;
}
$exposedRoutes[$name] = [
'tokens' => $compiledRoute->getTokens(),
'defaults' => $defaults,
'requirements' => $route->getRequirements(),
'hosttokens' => method_exists($compiledRoute, 'getHostTokens') ? $compiledRoute->getHostTokens() : [],
'methods' => $route->getMethods(),
'schemes' => $route->getSchemes(),
];
}
return $exposedRoutes;
}
public function setRoutes(RouteCollection $routes): void
{
$this->routes = $routes;
}
public function getBaseUrl(): string
{
return $this->baseUrl;
}
public function setBaseUrl(string $baseUrl): void
{
$this->baseUrl = $baseUrl;
}
public function getPrefix(): ?string
{
return $this->prefix;
}
public function setPrefix(?string $prefix): void
{
$this->prefix = $prefix;
}
public function getHost(): ?string
{
return $this->host;
}
public function setHost(?string $host): void
{
$this->host = $host;
}
public function getPort(): ?string
{
return $this->port;
}
public function setPort(?string $port): void
{
$this->port = $port;
}
public function getScheme(): ?string
{
return $this->scheme;
}
public function setScheme(?string $scheme): void
{
$this->scheme = $scheme;
}
public function getLocale(): ?string
{
return $this->locale;
}
public function setLocale(?string $locale): void
{
$this->locale = $locale;
}
public function getDomains(): array
{
return $this->domains;
}
public function setDomains(array $domains): void
{
$this->domains = $domains;
}
}

View File

@@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
/*
* This file is part of the FOSJsRoutingBundle package.
*
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\JsRoutingBundle\Serializer\Denormalizer;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
class RouteCollectionDenormalizer implements DenormalizerInterface
{
/**
* {@inheritDoc}
*/
public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): RouteCollection
{
$collection = new RouteCollection();
foreach ($data as $name => $values) {
$collection->add($name, new Route(
$values['path'],
$values['defaults'],
$values['requirements'],
$values['options'],
$values['host'],
$values['schemes'],
$values['methods'],
$values['condition']
));
}
return $collection;
}
/**
* {@inheritDoc}
*/
public function supportsDenormalization(mixed $data, string $type, ?string $format = null, array $context = []): bool
{
if (!is_array($data)) {
return false;
}
if (count($data) < 1) {
return true;
}
$values = current($data);
foreach (['path', 'defaults', 'requirements', 'options', 'host', 'schemes', 'methods', 'condition'] as $key) {
if (!isset($values[$key])) {
return false;
}
}
return true;
}
public function getSupportedTypes(?string $format): array
{
return ['*' => false];
}
}

View File

@@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
/*
* This file is part of the FOSJsRoutingBundle package.
*
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\JsRoutingBundle\Serializer\Normalizer;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
/**
* Class RouteCollectionNormalizer.
*/
class RouteCollectionNormalizer implements NormalizerInterface
{
/**
* {@inheritDoc}
*/
public function normalize(mixed $object, ?string $format = null, array $context = []): array
{
$collection = [];
foreach ($object->all() as $name => $route) {
$collection[$name] = [
'path' => $route->getPath(),
'host' => $route->getHost(),
'defaults' => $route->getDefaults(),
'requirements' => $route->getRequirements(),
'options' => $route->getOptions(),
'schemes' => $route->getSchemes(),
'methods' => $route->getMethods(),
'condition' => method_exists($route, 'getCondition') ? $route->getCondition() : '',
];
}
return $collection;
}
/**
* {@inheritDoc}
*/
public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool
{
return $data instanceof RouteCollection;
}
public function getSupportedTypes(?string $format): array
{
return [RouteCollection::class => true];
}
}

View File

@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
/*
* This file is part of the FOSJsRoutingBundle package.
*
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\JsRoutingBundle\Serializer\Normalizer;
use FOS\JsRoutingBundle\Response\RoutesResponse;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
/**
* Class RoutesResponseNormalizer.
*/
class RoutesResponseNormalizer implements NormalizerInterface
{
/**
* {@inheritDoc}
*/
public function normalize(mixed $object, ?string $format = null, array $context = []): array
{
return [
'base_url' => $object->getBaseUrl(),
'routes' => $object->getRoutes(),
'prefix' => $object->getPrefix(),
'host' => $object->getHost(),
'port' => $object->getPort(),
'scheme' => $object->getScheme(),
'locale' => $object->getLocale(),
];
}
/**
* {@inheritDoc}
*/
public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool
{
return $data instanceof RoutesResponse;
}
public function getSupportedTypes(?string $format): array
{
return [RoutesResponse::class => true];
}
}

View File

@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
/*
* This file is part of the FOSJsRoutingBundle package.
*
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\JsRoutingBundle\Util;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\EventListener\AbstractSessionListener;
class CacheControlConfig
{
public function __construct(private array $parameters = [])
{
}
public function apply(Response $response): void
{
if (empty($this->parameters['enabled'])) {
return;
}
$response->headers->set(AbstractSessionListener::NO_AUTO_CACHE_CONTROL_HEADER, 'true');
$this->parameters['public'] ? $response->setPublic() : $response->setPrivate();
if (is_int($this->parameters['maxage'])) {
$response->setMaxAge($this->parameters['maxage']);
}
if (is_int($this->parameters['smaxage'])) {
$response->setSharedMaxAge($this->parameters['smaxage']);
}
if (null !== $this->parameters['expires']) {
$response->setExpires(new \DateTime($this->parameters['expires']));
}
if (!empty($this->parameters['vary'])) {
$response->setVary($this->parameters['vary']);
}
}
}