diff --git a/.dev/phpcs.xml b/.dev/phpcs.xml new file mode 100644 index 0000000..5821512 --- /dev/null +++ b/.dev/phpcs.xml @@ -0,0 +1,285 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.dev/phpstan/phpstan.neon b/.dev/phpstan/phpstan.neon new file mode 100755 index 0000000..eefcbfc --- /dev/null +++ b/.dev/phpstan/phpstan.neon @@ -0,0 +1,25 @@ +parameters: + tmpDir: %currentWorkingDirectory%/temp/phpstan/ + + paths: + - %currentWorkingDirectory%/src/ + - %currentWorkingDirectory%/tests/ + + excludePaths: + - %currentWorkingDirectory%/tests/temp/* + + level: 8 + + ignoreErrors: + - + message: '#Parameter \#1 \$filter of method WebLoader\\Compiler::addFilter\(\) expects callable\(\): mixed, 4 given\.#' + path: %currentWorkingDirectory%/tests/CompilerTest.php + - + message: '#Parameter \#1 \$filter of method WebLoader\\Compiler::addFileFilter\(\) expects callable\(\): mixed, 4 given\.#' + path: %currentWorkingDirectory%/tests/CompilerTest.php + - + message: '#Access to an undefined property WebLoader\\Filter\\VariablesFilter::\$bar\.#' + path: %currentWorkingDirectory%/tests/Filter/VariablesFilterTest.php + - + message: '#Property WebLoader\\Nette\\Diagnostics\\Panel::\$root is never read, only written.#' + path: %currentWorkingDirectory%/src/Nette/Diagnostics/Panel.php diff --git a/.editorconfig b/.editorconfig new file mode 100755 index 0000000..fb6c057 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# EditorConfig is awesome: http://EditorConfig.org + +root = true + +[*] +end_of_line = lf +insert_final_newline = true +indent_style = tab +indent_size = 3 +charset = utf-8 +trim_trailing_whitespace = true + +[{*.svg,.htaccess,*.expected}] +insert_final_newline = false diff --git a/.gitignore b/.gitignore index 45bdece..bfbbf45 100755 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,16 @@ -vendor +# Masks +*.bak + +# Files composer.lock -.idea -.editorconfig +.phpunit.result.cache +tests/.phpunit.cache/ + +# Dirs +vendor/ +temp/ +.idea/ + +# Custom +run +composer.phar diff --git a/README.md b/README.md index 354ab36..b52187f 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,13 @@ -WebLoader [![Build Status](https://secure.travis-ci.org/janmarek/WebLoader.png?branch=master)](http://travis-ci.org/janmarek/WebLoader) +WebLoader ======================= -Component for CSS and JS files loading +Component for CSS and JS files loading. -Author: Jan Marek +Author: [Jan Marek](https://github.com/janmarek) Licence: MIT +Updated for Nette 3/3.1 and PHP 8.0 by [Gappa](https://github.com/Gappa). + Example ------- @@ -75,9 +77,6 @@ webloader: js: default: - remoteFiles: - - http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js - - http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js files: - %appDir%/../libs/nette/nette/client-side/netteForms.js - web.js diff --git a/WebLoader/File.php b/WebLoader/File.php deleted file mode 100644 index bc21f6a..0000000 --- a/WebLoader/File.php +++ /dev/null @@ -1,47 +0,0 @@ -file = $file; - $this->lastModified = $lastModified; - $this->sourceFiles = $sourceFiles; - } - - - public function getFile(): string - { - return $this->file; - } - - - public function getLastModified(): ?int - { - return $this->lastModified; - } - - - public function getSourceFiles(): array - { - return $this->sourceFiles; - } -} diff --git a/WebLoader/FileNotFoundException.php b/WebLoader/FileNotFoundException.php deleted file mode 100644 index bda3b4b..0000000 --- a/WebLoader/FileNotFoundException.php +++ /dev/null @@ -1,14 +0,0 @@ -bin = $bin; - } - - - public function __invoke(string $code, Compiler $loader, ?string $file = null): string - { - $file = (string) $file; - - if (pathinfo($file, PATHINFO_EXTENSION) === 'coffee') { - $code = $this->compileCoffee($code); - } - - return $code; - } - - - public function compileCoffee(string $source, ?bool $bare = null): string - { - if ($bare === null) { - $bare = $this->bare; - } - - $cmd = $this->bin . ' -p -s' . ($bare ? ' -b' : ''); - - return Process::run($cmd, $source); - } -} diff --git a/WebLoader/Filter/LessFilter.php b/WebLoader/Filter/LessFilter.php deleted file mode 100644 index c775d48..0000000 --- a/WebLoader/Filter/LessFilter.php +++ /dev/null @@ -1,51 +0,0 @@ -lc = $lc; - } - - - private function getLessC(): lessc - { - // lazy loading - if (empty($this->lc)) { - $this->lc = new lessc(); - } - - return clone $this->lc; - } - - - /** - * Invoke filter - */ - public function __invoke(string $code, Compiler $loader, string $file): string - { - if (pathinfo($file, PATHINFO_EXTENSION) === 'less') { - $lessc = $this->getLessC(); - $lessc->importDir = pathinfo($file, PATHINFO_DIRNAME) . '/'; - return $lessc->compile($code); - } - - return $code; - } -} diff --git a/WebLoader/Filter/PHPCoffeeScriptFilter.php b/WebLoader/Filter/PHPCoffeeScriptFilter.php deleted file mode 100644 index 988022f..0000000 --- a/WebLoader/Filter/PHPCoffeeScriptFilter.php +++ /dev/null @@ -1,38 +0,0 @@ -compileCoffee($code, $file); - } - - return $code; - } - - - public function compileCoffee(string $source, ?string $file): string - { - try { - return Compiler::compile($source, ['filename' => $file]); - } catch (Throwable $e) { - throw new WebLoaderException('CoffeeScript Filter Error: ' . $e->getMessage(), 0, $e); - } - } -} diff --git a/WebLoader/Filter/ScssFilter.php b/WebLoader/Filter/ScssFilter.php deleted file mode 100644 index 6a61483..0000000 --- a/WebLoader/Filter/ScssFilter.php +++ /dev/null @@ -1,50 +0,0 @@ -sc = $sc; - } - - - private function getScssC(): Compiler - { - // lazy loading - if (empty($this->sc)) { - $this->sc = new Compiler(); - } - - return $this->sc; - } - - - public function __invoke(string $code, \WebLoader\Compiler $loader, string $file): string - { - $file = (string) $file; - - if (pathinfo($file, PATHINFO_EXTENSION) === 'scss') { - $this->getScssC()->setImportPaths(['', pathinfo($file, PATHINFO_DIRNAME) . '/']); - return $this->getScssC()->compile($code); - } - - return (string) $code; - } -} diff --git a/WebLoader/InvalidArgumentException.php b/WebLoader/InvalidArgumentException.php deleted file mode 100644 index 9303a36..0000000 --- a/WebLoader/InvalidArgumentException.php +++ /dev/null @@ -1,14 +0,0 @@ - - */ -class CompilationException extends \WebLoader\WebLoaderException -{ -} diff --git a/WebLoader/Nette/Extension.php b/WebLoader/Nette/Extension.php deleted file mode 100755 index 20ce4d4..0000000 --- a/WebLoader/Nette/Extension.php +++ /dev/null @@ -1,277 +0,0 @@ - Expect::structure([ - 'checkLastModified' => Expect::bool(true), - 'debug' => Expect::bool(false), - 'sourceDir' => Expect::string('%wwwDir%/js'), - 'tempDir' => Expect::string('%wwwDir%/' . self::DEFAULT_TEMP_PATH), - 'tempPath' => Expect::string(self::DEFAULT_TEMP_PATH), - 'files' => Expect::array(), - 'watchFiles' => Expect::array(), - 'remoteFiles' => Expect::array(), - 'filters' => Expect::array(), - 'fileFilters' => Expect::array(), - 'joinFiles' => Expect::bool(true), - 'async' => Expect::bool(false), - 'defer' => Expect::bool(false), - 'nonce' => Expect::string()->nullable(), - 'absoluteUrl' => Expect::bool(false), - 'namingConvention' => Expect::string('@' . $this->prefix('jsNamingConvention')), - ]), - 'cssDefaults' => Expect::structure([ - 'checkLastModified' => Expect::bool(true), - 'debug' => Expect::bool(false), - 'sourceDir' => Expect::string('%wwwDir%/css')->dynamic(), - 'tempDir' => Expect::string('%wwwDir%/' . self::DEFAULT_TEMP_PATH), - 'tempPath' => Expect::string(self::DEFAULT_TEMP_PATH), - 'files' => Expect::array(), - 'watchFiles' => Expect::array(), - 'remoteFiles' => Expect::array(), - 'filters' => Expect::array(), - 'fileFilters' => Expect::array(), - 'joinFiles' => Expect::bool(true), - 'async' => Expect::bool(false), - 'defer' => Expect::bool(false), - 'nonce' => Expect::string()->nullable(), - 'absoluteUrl' => Expect::bool(false), - 'namingConvention' => Expect::string('@' . $this->prefix('cssNamingConvention')), - ]), - 'js' => Expect::array(), - 'css' => Expect::array(), - 'debugger' => Expect::bool('%debugMode%'), - ]); - } - - - public function loadConfiguration(): void - { - $builder = $this->getContainerBuilder(); - - $params = $this->getContainerBuilder()->parameters; - $json = json_encode($this->getConfig()); - $config = json_decode((string) $json, true); - $config = Helpers::expand($config, $params); - - $builder->addDefinition($this->prefix('cssNamingConvention')) - ->setFactory('WebLoader\DefaultOutputNamingConvention::createCssConvention'); - - $builder->addDefinition($this->prefix('jsNamingConvention')) - ->setFactory('WebLoader\DefaultOutputNamingConvention::createJsConvention'); - - if ($config['debugger']) { - $builder->addDefinition($this->prefix('tracyPanel')) - ->setClass('WebLoader\Nette\Diagnostics\Panel') - ->setArguments([$params['appDir']]); - } - - $builder->parameters['webloader'] = $config; - - $loaderFactoryTempPaths = []; - - foreach (['css', 'js'] as $type) { - foreach ($config[$type] as $name => $wlConfig) { - /** @var array $wlConfig */ - $wlConfig = \Nette\Schema\Helpers::merge($wlConfig, $config[$type . 'Defaults']); - $this->addWebLoader($builder, $type . ucfirst($name), $wlConfig); - $loaderFactoryTempPaths[strtolower($name)] = $wlConfig['tempPath']; - - if (!is_dir($wlConfig['tempDir']) || !is_writable($wlConfig['tempDir'])) { - throw new CompilationException(sprintf("You must create a writable directory '%s'", $wlConfig['tempDir'])); - } - } - } - - $builder->addDefinition($this->prefix('factory')) - ->setFactory('WebLoader\Nette\LoaderFactory', [$loaderFactoryTempPaths, $this->name]); - - if (class_exists('Symfony\Component\Console\Command\Command')) { - $builder->addDefinition($this->prefix('generateCommand')) - ->setClass('WebLoader\Nette\SymfonyConsole\GenerateCommand') - ->addTag('kdyby.console.command'); - } - } - - - private function addWebLoader(ContainerBuilder $builder, string $name, array $config): void - { - $filesServiceName = $this->prefix($name . 'Files'); - - $files = $builder->addDefinition($filesServiceName) - ->setClass('WebLoader\FileCollection') - ->setArguments([$config['sourceDir']]); - - foreach ($this->findFiles($config['files'], $config['sourceDir']) as $file) { - $files->addSetup('addFile', [$file]); - } - - foreach ($this->findFiles($config['watchFiles'], $config['sourceDir']) as $file) { - $files->addSetup('addWatchFile', [$file]); - } - - $files->addSetup('addRemoteFiles', [$config['remoteFiles']]); - - $compiler = $builder->addDefinition($this->prefix($name . 'Compiler')) - ->setClass('WebLoader\Compiler') - ->setArguments([ - '@' . $filesServiceName, - $config['namingConvention'], - $config['tempDir'], - ]); - - $compiler - ->addSetup('setJoinFiles', [$config['joinFiles']]) - ->addSetup('setAsync', [$config['async']]) - ->addSetup('setDefer', [$config['defer']]) - ->addSetup('setNonce', [$config['nonce']]) - ->addSetup('setAbsoluteUrl', [$config['absoluteUrl']]); - - if ($builder->parameters['webloader']['debugger']) { - $compiler->addSetup('@' . $this->prefix('tracyPanel') . '::addLoader', [ - $name, - '@' . $this->prefix($name . 'Compiler'), - ]); - } - - foreach ($config['filters'] as $filter) { - $compiler->addSetup('addFilter', [$filter]); - } - - foreach ($config['fileFilters'] as $filter) { - $compiler->addSetup('addFileFilter', [$filter]); - } - - if (isset($config['debug']) && $config['debug']) { - $compiler->addSetup('enableDebugging'); - } - - $compiler->addSetup('setCheckLastModified', [$config['checkLastModified']]); - - // todo css media - } - - - // I have no clue what this is supposed to do... - // public function afterCompile(Nette\PhpGenerator\ClassType $class): void - // { - // $meta = $class->getProperty('meta'); - // if (array_key_exists('webloader\\nette\\loaderfactory', $meta->value['types'])) { - // $meta->value['types']['webloader\\loaderfactory'] = $meta->value['types']['webloader\\nette\\loaderfactory']; - // } - // if (array_key_exists('WebLoader\\Nette\\LoaderFactory', $meta->value['types'])) { - // $meta->value['types']['WebLoader\\LoaderFactory'] = $meta->value['types']['WebLoader\\Nette\\LoaderFactory']; - // } - // - // $init = $class->methods['initialize']; - // $init->addBody('if (!class_exists(?, ?)) class_alias(?, ?);', ['WebLoader\\LoaderFactory', false, 'WebLoader\\Nette\\LoaderFactory', 'WebLoader\\LoaderFactory']); - // } - - - public function install(Configurator $configurator): void - { - $self = $this; - $configurator->onCompile[] = function ($configurator, Compiler $compiler) use ($self): void { - $compiler->addExtension($self::EXTENSION_NAME, $self); - }; - } - - - private function findFiles(array $filesConfig, string $sourceDir): array - { - $normalizedFiles = []; - - /** @var array|string $file */ - foreach ($filesConfig as $file) { - // finder support - if (is_array($file) && isset($file['files']) && (isset($file['in']) || isset($file['from']))) { - $finder = Finder::findFiles($file['files']); - - if (isset($file['exclude'])) { - $finder->exclude($file['exclude']); - } - - if (isset($file['in'])) { - $finder->in(is_dir($file['in']) ? $file['in'] : $sourceDir . DIRECTORY_SEPARATOR . $file['in']); - } else { - $finder->from(is_dir($file['from']) ? $file['from'] : $sourceDir . DIRECTORY_SEPARATOR . $file['from']); - } - - $foundFilesList = []; - foreach ($finder as $foundFile) { - /** @var SplFileInfo $foundFile */ - $foundFilesList[] = $foundFile->getPathname(); - } - - natsort($foundFilesList); - - /** @var string $foundFilePathname */ - foreach ($foundFilesList as $foundFilePathname) { - $normalizedFiles[] = $foundFilePathname; - } - - } else { - if (is_string($file)) { - $this->checkFileExists($file, $sourceDir); - $normalizedFiles[] = $file; - } - } - } - - return $normalizedFiles; - } - - - protected function checkFileExists(string $file, string $sourceDir): void - { - if (!$this->fileExists($file)) { - $tmp = rtrim($sourceDir, '/\\') . DIRECTORY_SEPARATOR . $file; - if (!$this->fileExists($tmp)) { - throw new FileNotFoundException(sprintf("Neither '%s' or '%s' was found", $file, $tmp)); - } - } - } - - - /** - * Some servers seem to have problems under cron user with open_basedir restriction when using relative paths - */ - protected function fileExists(string $file): bool - { - $file = realpath($file); - - if ($file === false) { - $file = ''; - } - - return file_exists($file); - } -} diff --git a/WebLoader/Nette/LoaderFactory.php b/WebLoader/Nette/LoaderFactory.php deleted file mode 100755 index 450edde..0000000 --- a/WebLoader/Nette/LoaderFactory.php +++ /dev/null @@ -1,59 +0,0 @@ -httpRequest = $httpRequest; - $this->serviceLocator = $serviceLocator; - $this->tempPaths = $tempPaths; - $this->extensionName = $extensionName; - } - - - public function createCssLoader(string $name, bool $appendLastModified = false): CssLoader - { - /** @var Compiler $compiler */ - $compiler = $this->serviceLocator->getService($this->extensionName . '.css' . ucfirst($name) . 'Compiler'); - return new CssLoader($compiler, $this->formatTempPath($name, $compiler->isAbsoluteUrl()), $appendLastModified); - } - - - public function createJavaScriptLoader(string $name, bool $appendLastModified = false): JavaScriptLoader - { - /** @var Compiler $compiler */ - $compiler = $this->serviceLocator->getService($this->extensionName . '.js' . ucfirst($name) . 'Compiler'); - return new JavaScriptLoader($compiler, $this->formatTempPath($name, $compiler->isAbsoluteUrl()), $appendLastModified); - } - - - private function formatTempPath(string $name, $absoluteUrl = false): string - { - $lName = strtolower($name); - $tempPath = isset($this->tempPaths[$lName]) ? $this->tempPaths[$lName] : Extension::DEFAULT_TEMP_PATH; - $method = $absoluteUrl ? 'getBaseUrl' : 'getBasePath'; - return rtrim($this->httpRequest->getUrl()->{$method}(), '/') . '/' . $tempPath; - } -} diff --git a/WebLoader/Nette/SymfonyConsole/GenerateCommand.php b/WebLoader/Nette/SymfonyConsole/GenerateCommand.php deleted file mode 100755 index 7b121ba..0000000 --- a/WebLoader/Nette/SymfonyConsole/GenerateCommand.php +++ /dev/null @@ -1,61 +0,0 @@ -findByType(WebLoader\Compiler::class); - foreach ($compilers as $compilerName) { - $this->compilers[$compilerName] = $container->getService($compilerName); - } - } - - - protected function configure(): void - { - $this->setName(self::$defaultName) - ->setDescription('Generates files.') - ->addOption('force', 'f', InputOption::VALUE_NONE, 'Generate if not modified.'); - } - - - protected function execute(InputInterface $input, OutputInterface $output): void - { - $force = $input->getOption('force'); - - $nofiles = true; - foreach ($this->compilers as $compiler) { - $files = $compiler->generate(!$force); - foreach ($files as $file) { - $output->writeln($file->file); - $nofiles = false; - } - } - - if ($nofiles) { - $output->writeln('No files generated.'); - } - } -} diff --git a/WebLoader/Nette/WebLoader.php b/WebLoader/Nette/WebLoader.php deleted file mode 100755 index a3ec962..0000000 --- a/WebLoader/Nette/WebLoader.php +++ /dev/null @@ -1,109 +0,0 @@ -compiler = $compiler; - $this->tempPath = $tempPath; - $this->appendLastModified = $appendLastModified; - } - - - public function getCompiler(): Compiler - { - return $this->compiler; - } - - - public function setCompiler(Compiler $compiler): void - { - $this->compiler = $compiler; - } - - - public function getTempPath(): string - { - return $this->tempPath; - } - - - public function setTempPath(string $tempPath): void - { - $this->tempPath = $tempPath; - } - - - /** - * Get html element including generated content - */ - abstract public function getElement(string $source): Html; - - - /** - * Generate compiled file(s) and render link(s) - */ - public function render(): void - { - $hasArgs = func_num_args() > 0; - - if ($hasArgs) { - $backup = $this->compiler->getFileCollection(); - $newFiles = new FileCollection($backup->getRoot()); - $newFiles->addFiles(func_get_args()); - $this->compiler->setFileCollection($newFiles); - } - - // remote files - foreach ($this->compiler->getFileCollection()->getRemoteFiles() as $file) { - echo $this->getElement($file), PHP_EOL; - } - - foreach ($this->compiler->generate() as $file) { - echo $this->getElement($this->getGeneratedFilePath($file)), PHP_EOL; - } - - if ($hasArgs && !empty($backup)) { - $this->compiler->setFileCollection($backup); - } - } - - - protected function getGeneratedFilePath(File $file) - { - $path = $this->tempPath . '/' . $file->getFile(); - - if ($this->appendLastModified) { - $path .= '?' . $file->getLastModified(); - } - - return $path; - } -} diff --git a/WebLoader/Path.php b/WebLoader/Path.php deleted file mode 100755 index 7b95eb6..0000000 --- a/WebLoader/Path.php +++ /dev/null @@ -1,29 +0,0 @@ -= 7.1", - "nette/application": "^3.0", + "php": "^8.3", + "nette/application": "^3.1", "nette/di": "^3.0", - "nette/utils": "^3.0", - "ext-json": "*" + "nette/utils": "^4.0", + "ext-json": "*", + "nette/schema": "^1.2", + "nette/finder": "^3.0", + "latte/latte": "^3.0", + "tracy/tracy": "^2.8" }, "suggest": { - "oyejorge/less.php": "LESS compiler written in PHP.", - "leafo/scssphp": "SCSS compiler written in PHP.", - "joseki/webloader-filters": "CSSMin & JSMin filters written in PHP.", - "coffeescript/coffeescript": "CoffeeScript compiler written in PHP." + "wikimedia/less.php": "LESS compiler written in PHP.", + "scssphp/scssphp": "SCSS compiler written in PHP.", + "tedivm/jshrink": "Javascript Minifier built in PHP", + "tubalmartin/cssmin": "A PHP port of the YUI CSS compressor", + "symfony/console": "For pre-generating files from CLI", + "nette/safe-stream": "Atomic and safe manipulation with files via native PHP functions." }, "require-dev": { - "nette/application": "^3.0", - "nette/bootstrap": "^3.0", - "nette/caching": "^3.0", + "nette/bootstrap": "^3.1", + "nette/caching": "^3.1", "nette/component-model": "^3.0", - "nette/database": "^3.0", - "nette/deprecated": "^2.3", - "nette/di": "^3.0", - "nette/finder": "^2.5", - "nette/forms": "^3.0", "nette/http": "^3.0", - "nette/mail": "^3.0", "nette/neon": "^3.0", - "nette/php-generator": "^3.0", - "nette/reflection": "^2.4", - "nette/robot-loader": "^3.0", - "nette/safe-stream": "^2.4", - "nette/security": "^3.0", - "nette/tokenizer": "^3.0", - "nette/utils": "^3.0", - "latte/latte": "^2.5", - "tracy/tracy": "^2.6", - "oyejorge/less.php": "^1.7", - "leafo/scssphp": "^0.7", - "kylekatarnls/coffeescript": "1.3.*", + "nette/robot-loader": "^3.0 || ^4.0", + "wikimedia/less.php": "^5.0.0", + "scssphp/scssphp": "^2.0.0", "mockery/mockery": "1.*", - "phpunit/phpunit": "7.*", - "jakub-onderka/php-parallel-lint": "~0.7", - "phpstan/phpstan-shim": "^0.11.0", - "phpstan/phpstan-nette": "^0.11.0" + "phpunit/phpunit": "12.*", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0.0", + "phpstan/phpstan-nette": "^2.0.0", + "roave/security-advisories": "dev-master", + "symfony/console": "^4.2.9|^5.0.0|^6.0.0||^7.0.0", + "phpstan/phpstan-mockery": "^2.0.0", + "tubalmartin/cssmin": "^4.1", + "tedivm/jshrink": "^1.3", + "slevomat/coding-standard": "^8.15" + }, + "scripts": { + "phpstan": "@php ./vendor/bin/phpstan analyse --configuration ./phpstan/phpstan.neon --memory-limit 512M", + "tests": "@php ./vendor/bin/phpunit --configuration tests/phpunit.xml tests" }, - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" + "config": { + "allow-plugins": { + "phpstan/extension-installer": true, + "dealerdirect/phpcodesniffer-composer-installer": true } } } diff --git a/phpstan/phpstan b/phpstan/phpstan deleted file mode 100755 index 9e146d0..0000000 --- a/phpstan/phpstan +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -./vendor/bin/phpstan analyse --configuration ./phpstan/phpstan.neon diff --git a/phpstan/phpstan.neon b/phpstan/phpstan.neon deleted file mode 100755 index 331b515..0000000 --- a/phpstan/phpstan.neon +++ /dev/null @@ -1,13 +0,0 @@ -parameters: - autoload_directories: - - WebLoader/ - - paths: - - WebLoader/ - - level: 7 - - -includes: - - ../vendor/phpstan/phpstan-nette/extension.neon - - ../vendor/phpstan/phpstan-nette/rules.neon diff --git a/src/BatchCollection.php b/src/BatchCollection.php new file mode 100755 index 0000000..e225375 --- /dev/null +++ b/src/BatchCollection.php @@ -0,0 +1,41 @@ + */ + private array $batches = []; + + + public function __construct() + { + } + + + /** @return array */ + public function getBatches(): array + { + return $this->batches; + } + + + /** + * @param array $batch + * @throws BatchAlreadyExistsException + */ + public function addBatch(string $type, string $name, array $batch): void + { + if (isset($this->batches[$type][$name])) { + throw new BatchAlreadyExistsException( + sprintf("Batch '%s' of type '%s', already exists.", $name, $type) + ); + } + + $this->batches[$type][$name] = $batch; + } +} diff --git a/WebLoader/Compiler.php b/src/Compiler.php similarity index 70% rename from WebLoader/Compiler.php rename to src/Compiler.php index 38a06dc..6319598 100755 --- a/WebLoader/Compiler.php +++ b/src/Compiler.php @@ -1,9 +1,15 @@ */ + private array $filters = []; - /** @var bool */ - private $async = false; + /** @var list */ + private array $fileFilters = []; - /** @var bool */ - private $defer = false; - - /** @var string|null */ - private $nonce; - - /** @var bool */ - private $absoluteUrl = false; + private bool $checkLastModified = true; + private bool $debugging = false; + private bool $async = false; + private bool $defer = false; + private bool $absoluteUrl = false; + private ?string $nonce = null; public function __construct(IFileCollection $files, IOutputNamingConvention $convention, string $outputDir) @@ -62,7 +48,7 @@ public function __construct(IFileCollection $files, IOutputNamingConvention $con */ public static function createCssCompiler(IFileCollection $files, string $outputDir): self { - return new static($files, DefaultOutputNamingConvention::createCssConvention(), $outputDir); + return new self($files, DefaultOutputNamingConvention::createCssConvention(), $outputDir); } @@ -71,7 +57,7 @@ public static function createCssCompiler(IFileCollection $files, string $outputD */ public static function createJsCompiler(IFileCollection $files, string $outputDir): self { - return new static($files, DefaultOutputNamingConvention::createJsConvention(), $outputDir); + return new self($files, DefaultOutputNamingConvention::createJsConvention(), $outputDir); } @@ -115,24 +101,6 @@ public function setOutputDir(string $tempPath): void } - /** - * Get join files - */ - public function getJoinFiles(): bool - { - return $this->joinFiles; - } - - - /** - * Set join files - */ - public function setJoinFiles(bool $joinFiles): void - { - $this->joinFiles = $joinFiles; - } - - public function isAsync(): bool { return $this->async; @@ -183,6 +151,7 @@ public function setCheckLastModified(bool $checkLastModified): void /** * Get last modified timestamp of newest file + * @param list|null $files */ public function getLastModified(?array $files = null): int { @@ -192,6 +161,7 @@ public function getLastModified(?array $files = null): int $modified = 0; + /** @var string $file */ foreach ($files as $file) { $modified = max($modified, filemtime((string) realpath($file))); } @@ -202,6 +172,7 @@ public function getLastModified(?array $files = null): int /** * Get joined content of all files + * @param array|null $files */ public function getContent(?array $files = null): string { @@ -224,49 +195,44 @@ public function getContent(?array $files = null): string } - /** - * Load content and save file - */ - public function generate(): array + public function generate(): ?File { $files = $this->collection->getFiles(); if (!count($files)) { - return []; + return null; } - if ($this->joinFiles) { - $watchFiles = $this->checkLastModified ? array_unique(array_merge($files, $this->collection->getWatchFiles())) : []; - - return [ - $this->generateFiles($files, $watchFiles), - ]; - + if ($this->checkLastModified) { + $watchFiles = array_unique(array_merge($files, $this->collection->getWatchFiles())); + $watchFiles = array_values($watchFiles); } else { - $arr = []; - - foreach ($files as $file) { - $watchFiles = $this->checkLastModified ? array_unique(array_merge([$file], $this->collection->getWatchFiles())) : []; - $arr[] = $this->generateFiles([$file], $watchFiles); - } - - return $arr; + $watchFiles = []; } + + return $this->generateFiles($files, $watchFiles); } - protected function generateFiles(array $files, array $watchFiles = []) + /** + * @param list $files + * @param list $watchFiles + */ + protected function generateFiles(array $files, array $watchFiles = []): File { $name = $this->namingConvention->getFilename($files, $this); $path = $this->outputDir . '/' . $name; - $lastModified = $this->checkLastModified ? $this->getLastModified($watchFiles) : 0; + $lastModified = $this->checkLastModified + ? $this->getLastModified($watchFiles) + : 0; if (!file_exists($path) || $lastModified > filemtime($path) || $this->debugging === true) { - $outPath = in_array('nette.safe', stream_get_wrappers(), true) ? 'nette.safe://' . $path : $path; - file_put_contents($outPath, $this->getContent($files)); + // disabled: https://github.com/nette/safe-stream/pull/5 + // $outPath = in_array('nette.safe', stream_get_wrappers(), true) ? 'nette.safe://' . $path : $path; + FileSystem::write($path, $this->getContent($files)); } - return new File($name, (int) filemtime($path), $files); + return new File($path, $files); } @@ -312,6 +278,7 @@ public function addFilter(callable $filter): void } + /** @return list */ public function getFilters(): array { return $this->filters; @@ -324,6 +291,7 @@ public function addFileFilter(callable $filter): void } + /** @return list */ public function getFileFilters(): array { return $this->fileFilters; diff --git a/src/Contract/IBatchCollection.php b/src/Contract/IBatchCollection.php new file mode 100755 index 0000000..ffe2dab --- /dev/null +++ b/src/Contract/IBatchCollection.php @@ -0,0 +1,13 @@ + */ + public function getBatches(): array; + + /** @param array $batch */ + public function addBatch(string $type, string $name, array $batch): void; +} diff --git a/WebLoader/IFileCollection.php b/src/Contract/IFileCollection.php similarity index 61% rename from WebLoader/IFileCollection.php rename to src/Contract/IFileCollection.php index 1e49601..dcb62e8 100755 --- a/WebLoader/IFileCollection.php +++ b/src/Contract/IFileCollection.php @@ -1,8 +1,8 @@ */ public function getFiles(): array; - public function getRemoteFiles(): array; - + /** @return list */ public function getWatchFiles(): array; } diff --git a/WebLoader/IOutputNamingConvention.php b/src/Contract/IOutputNamingConvention.php similarity index 58% rename from WebLoader/IOutputNamingConvention.php rename to src/Contract/IOutputNamingConvention.php index 866ca08..252eac8 100755 --- a/WebLoader/IOutputNamingConvention.php +++ b/src/Contract/IOutputNamingConvention.php @@ -1,8 +1,10 @@ $files */ public function getFilename(array $files, Compiler $compiler): string; } diff --git a/src/Contract/IWebloaderAssetProvider.php b/src/Contract/IWebloaderAssetProvider.php new file mode 100755 index 0000000..f18af55 --- /dev/null +++ b/src/Contract/IWebloaderAssetProvider.php @@ -0,0 +1,10 @@ +> */ + public function getWebloaderAssets(): array; +} diff --git a/WebLoader/DefaultOutputNamingConvention.php b/src/DefaultOutputNamingConvention.php similarity index 79% rename from WebLoader/DefaultOutputNamingConvention.php rename to src/DefaultOutputNamingConvention.php index b470c18..ee821fb 100644 --- a/WebLoader/DefaultOutputNamingConvention.php +++ b/src/DefaultOutputNamingConvention.php @@ -1,27 +1,25 @@ setSuffix('.css'); return $convention; @@ -30,7 +28,7 @@ public static function createCssConvention(): self public static function createJsConvention(): self { - $convention = new static(); + $convention = new self; $convention->setSuffix('.js'); return $convention; @@ -77,6 +75,7 @@ public function setSuffix(string $suffix): void /** * Filename of generated file + * @param array $files */ public function getFilename(array $files, Compiler $compiler): string { @@ -84,7 +83,8 @@ public function getFilename(array $files, Compiler $compiler): string } - protected function createHash(array $files, Compiler $compiler) + /** @param array $files */ + protected function createHash(array $files, Compiler $compiler): string { $parts = $files; foreach ($files as $file) { diff --git a/src/Enum/RenderMode.php b/src/Enum/RenderMode.php new file mode 100644 index 0000000..918998d --- /dev/null +++ b/src/Enum/RenderMode.php @@ -0,0 +1,11 @@ + + */ +class CompilationException extends \WebLoader\Exception\WebLoaderException +{ +} diff --git a/src/Exception/FileNotFoundException.php b/src/Exception/FileNotFoundException.php new file mode 100644 index 0000000..822cfc3 --- /dev/null +++ b/src/Exception/FileNotFoundException.php @@ -0,0 +1,15 @@ + */ + private array $sourceFiles; + + + /** @param array $sourceFiles */ + public function __construct( + string $path, + array $sourceFiles, + ) { + $this->file = new SplFileInfo($path); + $this->sourceFiles = $sourceFiles; + } + + + public function getFileName(): string + { + return $this->file->getBasename(); + } + + + public function getPath(): string + { + return $this->file->getPathname(); + } + + + public function getLastModified(): int + { + return $this->file->getMTime(); + } + + + /** @return array */ + public function getSourceFiles(): array + { + return $this->sourceFiles; + } +} diff --git a/WebLoader/FileCollection.php b/src/FileCollection.php similarity index 57% rename from WebLoader/FileCollection.php rename to src/FileCollection.php index 9da7fe6..dad99d5 100755 --- a/WebLoader/FileCollection.php +++ b/src/FileCollection.php @@ -1,44 +1,34 @@ */ + private array $files = []; - /** @var string */ - private $root; + /** @var array */ + private array $watchFiles = []; - /** @var array */ - private $files = []; - /** @var array */ - private $watchFiles = []; - - /** @var array */ - private $remoteFiles = []; - - - /** - * @param string|null $root files root for relative paths - */ - public function __construct(?string $root = null) + public function __construct(private readonly string $root) { - $this->root = (string) $root; } - /** - * Get file list - */ + /** @return list */ public function getFiles(): array { return array_values($this->files); @@ -66,7 +56,11 @@ public function cannonicalizePath(string $path): string } - public function addFile($file): void + /** + * @param SplFileInfo|string $file + * @throws FileNotFoundException + */ + public function addFile(SplFileInfo|string $file): void { $file = $this->cannonicalizePath((string) $file); @@ -80,9 +74,9 @@ public function addFile($file): void /** * Add files - * @param array|Traversable $files array list of files + * @param iterable $files array list of files */ - public function addFiles($files): void + public function addFiles(iterable $files): void { foreach ($files as $file) { $this->addFile($file); @@ -96,6 +90,7 @@ public function removeFile(string $file): void } + /** @param array $files */ public function removeFiles(array $files): void { $files = array_map([$this, 'cannonicalizePath'], $files); @@ -103,32 +98,6 @@ public function removeFiles(array $files): void } - /** - * Add file in remote repository (for example Google CDN). - * @param string $file URL address - */ - public function addRemoteFile(string $file): void - { - if (in_array($file, $this->remoteFiles, true)) { - return; - } - - $this->remoteFiles[] = $file; - } - - - /** - * Add multiple remote files - * @param array|Traversable $files - */ - public function addRemoteFiles($files): void - { - foreach ($files as $file) { - $this->addRemoteFile($file); - } - } - - /** * Remove all files */ @@ -136,13 +105,6 @@ public function clear(): void { $this->files = []; $this->watchFiles = []; - $this->remoteFiles = []; - } - - - public function getRemoteFiles(): array - { - return $this->remoteFiles; } @@ -166,9 +128,9 @@ public function addWatchFile(string $file): void /** * Add watch files - * @param array|Traversable $files array list of files + * @param iterable $files array list of files */ - public function addWatchFiles($files): void + public function addWatchFiles(iterable $files): void { foreach ($files as $file) { $this->addWatchFile($file); @@ -176,6 +138,7 @@ public function addWatchFiles($files): void } + /** @return list */ public function getWatchFiles(): array { return array_values($this->watchFiles); diff --git a/src/Filter/CssMinFilter.php b/src/Filter/CssMinFilter.php new file mode 100755 index 0000000..0b72468 --- /dev/null +++ b/src/Filter/CssMinFilter.php @@ -0,0 +1,16 @@ +run($code); + } +} diff --git a/WebLoader/Filter/CssUrlsFilter.php b/src/Filter/CssUrlsFilter.php similarity index 84% rename from WebLoader/Filter/CssUrlsFilter.php rename to src/Filter/CssUrlsFilter.php index fd505ef..02beef0 100755 --- a/WebLoader/Filter/CssUrlsFilter.php +++ b/src/Filter/CssUrlsFilter.php @@ -1,11 +1,11 @@ docRoot = Path::normalize($docRoot); if (!is_dir($this->docRoot)) { throw new InvalidArgumentException('Given document root is not directory.'); } - - $this->basePath = $basePath; } @@ -112,9 +107,7 @@ public function __invoke(string $code, Compiler $loader, ?string $file = null): $self = $this; - $return = preg_replace_callback($regexp, function ($matches) use ($self, $file) { - return "url('" . $self->absolutizeUrl($matches[2], $matches[1], $file) . "')"; - }, $code); + $return = preg_replace_callback($regexp, fn($matches) => "url('" . $self->absolutizeUrl($matches[2], $matches[1], $file) . "')", $code); return (string) $return; } diff --git a/src/Filter/JsMinFilter.php b/src/Filter/JsMinFilter.php new file mode 100755 index 0000000..4596033 --- /dev/null +++ b/src/Filter/JsMinFilter.php @@ -0,0 +1,21 @@ + */ + private array $env; - /** @var string */ - private $bin; - /** @var array */ - private $env; - - - public function __construct(string $bin = 'lessc', array $env = []) + /** @param array $env */ + public function __construct(private string $bin = 'lessc', array $env = []) { - $this->bin = $bin; $this->env = $env + $_ENV; unset($this->env['argv'], $this->env['argc']); } diff --git a/src/Filter/LessFilter.php b/src/Filter/LessFilter.php new file mode 100644 index 0000000..886b55b --- /dev/null +++ b/src/Filter/LessFilter.php @@ -0,0 +1,36 @@ +getLessParser(); + $parser->parseFile($file); + return $parser->getCss(); + } + + return $code; + } +} diff --git a/WebLoader/Filter/Process.php b/src/Filter/Process.php similarity index 79% rename from WebLoader/Filter/Process.php rename to src/Filter/Process.php index ea41bb9..b297a01 100644 --- a/WebLoader/Filter/Process.php +++ b/src/Filter/Process.php @@ -1,6 +1,6 @@ $env */ + public static function run( + string $cmd, + ?string $stdin = null, + ?string $cwd = null, + ?array $env = null + ): string { $descriptorspec = [ 0 => ['pipe', 'r'], // stdin 1 => ['pipe', 'w'], // stdout diff --git a/src/Filter/ScssFilter.php b/src/Filter/ScssFilter.php new file mode 100644 index 0000000..039afa9 --- /dev/null +++ b/src/Filter/ScssFilter.php @@ -0,0 +1,30 @@ +getCompiler(); + $compiler->setImportPaths([pathinfo($file, PATHINFO_DIRNAME) . '/']); + $result = $compiler->compileString($code); + return $result->getCss(); + } + + return $code; + } +} diff --git a/WebLoader/Filter/StylusFilter.php b/src/Filter/StylusFilter.php similarity index 66% rename from WebLoader/Filter/StylusFilter.php rename to src/Filter/StylusFilter.php index 330f180..7ac4b30 100755 --- a/WebLoader/Filter/StylusFilter.php +++ b/src/Filter/StylusFilter.php @@ -1,6 +1,6 @@ bin = $bin; } @@ -40,7 +32,7 @@ public function __invoke(string $code, Compiler $loader, ?string $file = null): try { $code = Process::run($cmd, $code); } catch (\RuntimeException $e) { - throw new \WebLoader\WebLoaderException('Stylus Filter Error', 0, $e); + throw new \WebLoader\Exception\WebLoaderException('Stylus Filter Error', 0, $e); } } diff --git a/WebLoader/Filter/TypeScriptFilter.php b/src/Filter/TypeScriptFilter.php similarity index 77% rename from WebLoader/Filter/TypeScriptFilter.php rename to src/Filter/TypeScriptFilter.php index 81b0c89..88764f2 100755 --- a/WebLoader/Filter/TypeScriptFilter.php +++ b/src/Filter/TypeScriptFilter.php @@ -1,6 +1,6 @@ */ + private array $env; - /** @var string|null */ - private $bin; - /** @var array|null */ - private $env; - - - public function __construct(string $bin = 'tsc', array $env = []) + /** @param array $env */ + public function __construct(private string $bin = 'tsc', array $env = []) { - $this->bin = $bin; $this->env = $env + $_ENV; unset($this->env['argv'], $this->env['argc']); } diff --git a/WebLoader/Filter/VariablesFilter.php b/src/Filter/VariablesFilter.php similarity index 56% rename from WebLoader/Filter/VariablesFilter.php rename to src/Filter/VariablesFilter.php index 65e0b84..e97895a 100644 --- a/WebLoader/Filter/VariablesFilter.php +++ b/src/Filter/VariablesFilter.php @@ -1,9 +1,11 @@ $variables */ + public function __construct(private array $variables = []) { foreach ($variables as $key => $value) { $this->$key = $value; @@ -34,30 +27,20 @@ public function __construct(array $variables = []) } - /** - * Set delimiter - * - * @return \WebLoader\Filter\VariablesFilter - */ public function setDelimiter(string $start, string $end): self { - $this->startVariable = (string) $start; - $this->endVariable = (string) $end; + $this->startVariable = $start; + $this->endVariable = $end; return $this; } - /** - * Invoke filter - */ public function __invoke(string $code): string { $start = $this->startVariable; $end = $this->endVariable; - $variables = array_map(function ($key) use ($start, $end) { - return $start . $key . $end; - }, array_keys($this->variables)); + $variables = array_map(fn($key) => $start . $key . $end, array_keys($this->variables)); $values = array_values($this->variables); @@ -76,15 +59,14 @@ public function __set(string $name, string $value): void /** * Magic get variable, do not call directly - * - * @throws \WebLoader\InvalidArgumentException + * @throws InvalidArgumentException */ public function &__get(string $name): string { if (array_key_exists($name, $this->variables)) { return $this->variables[$name]; } else { - throw new \WebLoader\InvalidArgumentException("Variable '$name' is not set."); + throw new InvalidArgumentException("Variable '$name' is not set."); } } } diff --git a/WebLoader/Nette/CssLoader.php b/src/Nette/CssLoader.php similarity index 60% rename from WebLoader/Nette/CssLoader.php rename to src/Nette/CssLoader.php index 7e02647..8b77dab 100755 --- a/WebLoader/Nette/CssLoader.php +++ b/src/Nette/CssLoader.php @@ -1,10 +1,12 @@ media; } @@ -40,7 +34,7 @@ public function getType(): string } - public function getTitle(): string + public function getTitle(): ?string { return $this->title; } @@ -80,7 +74,7 @@ public function setAlternate(bool $alternate): self } - public function getElement(string $source): Html + public function getElement(File $file): Html { if ($this->alternate) { $alternate = ' alternate'; @@ -94,7 +88,20 @@ public function getElement(string $source): Html $el->setAttribute('media', $this->media); $el->setAttribute('title', $this->title); $el->setAttribute('nonce', $this->getCompiler()->getNonce()); - $el->setAttribute('href', $source); + $el->setAttribute('href', $this->getGeneratedFilePath($file)); + + return $el; + } + + + public function getInlineElement(File $file): Html + { + $el = Html::el('style'); + $el->setAttribute('type', $this->type); + $el->setAttribute('media', $this->media); + $el->setAttribute('title', $this->title); + $el->setAttribute('nonce', $this->getCompiler()->getNonce()); + $el->setHtml(FileSystem::read($file->getPath())); return $el; } diff --git a/WebLoader/Nette/CssUrlFilter.php b/src/Nette/CssUrlFilter.php similarity index 92% rename from WebLoader/Nette/CssUrlFilter.php rename to src/Nette/CssUrlFilter.php index 17e5091..2733164 100755 --- a/WebLoader/Nette/CssUrlFilter.php +++ b/src/Nette/CssUrlFilter.php @@ -1,6 +1,6 @@ */ + public static array $types = [ 'css' => 'CSS files', 'js' => 'JavaScript files', 'less' => 'Less files', 'scss' => 'Sass files', - 'coffee' => 'CoffeeScript files', ]; /** @var Compiler[] */ - private $compilers = []; + private array $compilers = []; - /** @var array */ - private $size; + /** @var array{original: int, combined: int, ratio: float}|null */ + private ?array $size = null; - /** @var array */ - private $files; + /** @var array */ + private array $files; - /** @var array */ - private $sizes; + /** @var array */ + private array $sizes; - /** @var string */ - private $root; + private string $root; public function __construct(?string $appDir = null) { - $this->root = $appDir ? str_replace('\\', DIRECTORY_SEPARATOR, (string) realpath(dirname($appDir))) : ''; + $this->root = $appDir + ? str_replace('\\', DIRECTORY_SEPARATOR, (string) realpath(dirname($appDir))) + : ''; Debugger::getBar()->addPanel($this); } @@ -53,6 +51,8 @@ public function __construct(?string $appDir = null) /** * Registers a compiler. * + * @param string $name + * @param Compiler $compiler * @return Panel */ public function addLoader(string $name, Compiler $compiler): self @@ -64,6 +64,7 @@ public function addLoader(string $name, Compiler $compiler): self /** * Computes the info. + * @return array{original: int, combined: int, ratio: float} */ private function compute(): array { @@ -77,6 +78,10 @@ private function compute(): array ]; $this->files = $this->sizes = []; + /** + * @var string $name + * @var Compiler $compiler + */ foreach ($this->compilers as $name => $compiler) { $group = lcfirst(substr($name, $name[0] === 'c' ? 3 : 2)); @@ -89,33 +94,37 @@ private function compute(): array $compilerCombinedSize = 0; - /** @var File $generated */ - foreach ($compiler->generate() as $generated) { - $generatedSize = filesize($compiler->getOutputDir() . DIRECTORY_SEPARATOR . $generated->getFile()); - $size['combined'] += $generatedSize; - $compilerCombinedSize += $generatedSize; - - foreach ($generated->getSourceFiles() as $file) { - $extension = strtolower(pathinfo($file, PATHINFO_EXTENSION)); - $file = str_replace('\\', DIRECTORY_SEPARATOR, (string) realpath($file)); - - if (!isset($this->files[$group][$extension])) { - $this->files[$group][$extension] = []; - } - if (!isset($this->sizes[$group][$extension])) { - $this->sizes[$group][$extension] = ['original' => 0, 'combined' => 0]; - } - - $this->files[$group][$extension][] = [ - 'name' => basename($file), - 'full' => $file, - 'size' => $fileSize = filesize($file), - ]; - - $size['original'] += $fileSize; - $this->sizes[$group][$extension]['original'] += $fileSize; - $this->sizes[$group]['.']['original'] += $fileSize; + $generated = $compiler->generate(); + + if (null === $generated) { + continue; + } + + $generatedSize = filesize($compiler->getOutputDir() . DIRECTORY_SEPARATOR . $generated->getFileName()); + $size['combined'] += $generatedSize; + + $compilerCombinedSize += $generatedSize; + + foreach ($generated->getSourceFiles() as $file) { + $extension = strtolower(pathinfo($file, PATHINFO_EXTENSION)); + $file = str_replace('\\', DIRECTORY_SEPARATOR, (string) realpath($file)); + + if (!isset($this->files[$group][$extension])) { + $this->files[$group][$extension] = []; + } + if (!isset($this->sizes[$group][$extension])) { + $this->sizes[$group][$extension] = ['original' => 0, 'combined' => 0]; } + + $this->files[$group][$extension][] = [ + 'name' => basename($file), + 'full' => $file, + 'size' => $fileSize = filesize($file), + ]; + + $size['original'] += $fileSize; + $this->sizes[$group][$extension]['original'] += $fileSize; + $this->sizes[$group]['.']['original'] += $fileSize; } $this->sizes[$group]['.']['combined'] += $compilerCombinedSize; @@ -132,9 +141,7 @@ private function getTable(): string { $latte = new Latte\Engine; - $latte->addFilter('extension', function ($extension) { - return isset(self::$types[$extension]) ? self::$types[$extension] : $extension; - }); + $latte->addFilter('extension', fn($extension) => self::$types[$extension] ?? $extension); return $latte->renderToString(__DIR__ . '/panel.latte', [ 'files' => $this->files, @@ -149,7 +156,7 @@ private function getTable(): string */ public function getPanel(): string { - return $this->compute() ? $this->getTable() : ''; + return $this->compute() ? $this->getTable() : ''; //@phpstan-ignore ternary.alwaysTrue } @@ -160,9 +167,13 @@ public function getTab(): string { $this->compute(); + if (empty($this->size['combined'])) { + return ''; + } + return '' . '' - . Filters::bytes($this->size['combined']) + . (new Filters())->bytes($this->size['combined']) . ''; } } diff --git a/WebLoader/Nette/Diagnostics/panel.latte b/src/Nette/Diagnostics/panel.latte similarity index 100% rename from WebLoader/Nette/Diagnostics/panel.latte rename to src/Nette/Diagnostics/panel.latte diff --git a/src/Nette/Extension.php b/src/Nette/Extension.php new file mode 100644 index 0000000..b42baf0 --- /dev/null +++ b/src/Nette/Extension.php @@ -0,0 +1,384 @@ +appDir = $appDir; + $this->wwwDir = $wwwDir; + $this->debugMode = $debugMode; + $this->batchCollection = new BatchCollection; + } + + + private function getJsConfigSchema(bool $useDefaults = false): Schema + { + $checkLastModified = $useDefaults ? true : null; + $debug = $useDefaults ? false : null; + $sourceDir = $useDefaults ? ($this->wwwDir . '/js') : null; + $tempDir = $useDefaults ? ($this->wwwDir . '/' . self::DEFAULT_TEMP_PATH) : null; + $tempPath = $useDefaults ? self::DEFAULT_TEMP_PATH : null; + $async = $useDefaults ? false : null; + $defer = $useDefaults ? false : null; + $absoluteUrl = $useDefaults ? false : null; + $namingConvention = $useDefaults + ? ('@' . $this->prefix('jsNamingConvention')) + : null; + + return Expect::structure([ + 'checkLastModified' => Expect::bool($checkLastModified), + 'debug' => Expect::bool($debug), + 'sourceDir' => Expect::string($sourceDir), + 'tempDir' => Expect::string($tempDir), + 'tempPath' => Expect::string($tempPath), + 'files' => Expect::array(), + 'watchFiles' => Expect::array(), + 'filters' => Expect::array(), + 'fileFilters' => Expect::array(), + 'async' => Expect::bool($async), + 'defer' => Expect::bool($defer), + 'nonce' => Expect::string()->nullable(), + 'absoluteUrl' => Expect::bool($absoluteUrl), + 'namingConvention' => Expect::string($namingConvention), + ]); + } + + + private function getCssConfigSchema(bool $useDefaults = false): Schema + { + $checkLastModified = $useDefaults ? true : null; + $debug = $useDefaults ? false : null; + $sourceDir = $useDefaults ? ($this->wwwDir . '/css') : null; + $tempDir = $useDefaults ? ($this->wwwDir . '/' . self::DEFAULT_TEMP_PATH) : null; + $tempPath = $useDefaults ? self::DEFAULT_TEMP_PATH : null; + $async = $useDefaults ? false : null; + $defer = $useDefaults ? false : null; + $absoluteUrl = $useDefaults ? false : null; + $namingConvention = $useDefaults + ? ('@' . $this->prefix('cssNamingConvention')) + : null; + + return Expect::structure([ + 'checkLastModified' => Expect::bool($checkLastModified), + 'debug' => Expect::bool($debug), + 'sourceDir' => Expect::string($sourceDir), + 'tempDir' => Expect::string($tempDir), + 'tempPath' => Expect::string($tempPath), + 'files' => Expect::array(), + 'watchFiles' => Expect::array(), + 'filters' => Expect::array(), + 'fileFilters' => Expect::array(), + 'async' => Expect::bool($async), + 'defer' => Expect::bool($defer), + 'nonce' => Expect::string()->nullable(), + 'absoluteUrl' => Expect::bool($absoluteUrl), + 'namingConvention' => Expect::string($namingConvention), + ]); + } + + + public function getConfigSchema(): Schema + { + return Expect::structure([ + 'jsDefaults' => $this->getJsConfigSchema(true), + 'cssDefaults' => $this->getCssConfigSchema(true), + 'js' => Expect::arrayOf($this->getJsConfigSchema())->nullable(), + 'css' => Expect::arrayOf($this->getCssConfigSchema())->nullable(), + 'debugger' => Expect::bool($this->debugMode), + ]); + } + + + public function loadConfiguration(): void + { + $builder = $this->getContainerBuilder(); + $config = json_decode((string) json_encode($this->getConfig()), true); + + $builder->addDefinition($this->prefix('cssNamingConvention')) + ->setFactory('WebLoader\DefaultOutputNamingConvention::createCssConvention'); + + $builder->addDefinition($this->prefix('jsNamingConvention')) + ->setFactory('WebLoader\DefaultOutputNamingConvention::createJsConvention'); + + if ($config['debugger']) { + $builder->addDefinition($this->prefix('tracyPanel')) + ->setType(Panel::class) + ->setArguments([$this->appDir]); + } + + $builder->parameters['webloader'] = $config; + + $loaderFactoryTempPaths = []; + + $this->extractBatchesFromExtensions(); + $this->extractNormalBatches($config); + + $batchTypes = $this->batchCollection->getBatches(); + foreach ($batchTypes as $type => $batches) { + foreach ($batches as $name => $batch) { + $batch = array_filter($batch); + $batch = SchemaHelpers::merge($batch, $config[$type . 'Defaults']); + + if (!is_array($batch)) { + throw new CompilationException('Batch config not valid.'); + } + + $this->addWebLoader($builder, $type . ucfirst($name), $batch); + $loaderFactoryTempPaths[strtolower($name)] = $batch['tempPath']; + + if (!is_dir($batch['tempDir']) || !is_writable($batch['tempDir'])) { + throw new CompilationException(sprintf("You must create a writable directory '%s'", $batch['tempDir'])); + } + } + } + + $builder->addDefinition($this->prefix('factory')) + ->setType(LoaderFactory::class) + ->setArguments([$loaderFactoryTempPaths, $this->name]); + + if (class_exists('Symfony\Component\Console\Command\Command')) { + $builder->addDefinition($this->prefix('generateCommand')) + ->setType(GenerateCommand::class) + ->addTag('kdyby.console.command'); + } + } + + + /** + * @param ContainerBuilder $builder + * @param string $name + * @param array $config + * @return void + * @throws FileNotFoundException + */ + private function addWebLoader(ContainerBuilder $builder, string $name, array $config): void + { + $filesServiceName = $this->prefix($name . 'Files'); + + $files = $builder->addDefinition($filesServiceName) + ->setType(FileCollection::class) + ->setArguments([$config['sourceDir']]); + + foreach ($this->findFiles($config['files'], $config['sourceDir']) as $file) { + $files->addSetup('addFile', [$file]); + } + + foreach ($this->findFiles($config['watchFiles'], $config['sourceDir']) as $file) { + $files->addSetup('addWatchFile', [$file]); + } + + $compiler = $builder->addDefinition($this->prefix($name . 'Compiler')) + ->setType(WebloaderCompiler::class) + ->setArguments([ + '@' . $filesServiceName, + $config['namingConvention'], + $config['tempDir'], + ]); + + $compiler + ->addSetup('setAsync', [$config['async']]) + ->addSetup('setDefer', [$config['defer']]) + ->addSetup('setNonce', [$config['nonce']]) + ->addSetup('setAbsoluteUrl', [$config['absoluteUrl']]); + + if ($builder->parameters['webloader']['debugger']) { + $compiler->addSetup('@' . $this->prefix('tracyPanel') . '::addLoader', [ + $name, + '@' . $this->prefix($name . 'Compiler'), + ]); + } + + foreach ($config['filters'] as $filter) { + $compiler->addSetup('addFilter', [$filter]); + } + + foreach ($config['fileFilters'] as $filter) { + $compiler->addSetup('addFileFilter', [$filter]); + } + + if (isset($config['debug']) && $config['debug']) { + $compiler->addSetup('enableDebugging'); + } + + $compiler->addSetup('setCheckLastModified', [$config['checkLastModified']]); + + // todo css media + } + + + // I have no clue what this is supposed to do... + // public function afterCompile(ClassType $class): void + // { + // $types = $class->getProperty('types'); + // if (array_key_exists('webloader\\nette\\loaderfactory', $types)) { + // $types['webloader\\loaderfactory'] = $types['webloader\\nette\\loaderfactory']; + // } + // if (array_key_exists('WebLoader\\Nette\\LoaderFactory', $types)) { + // $types['WebLoader\\LoaderFactory'] = $types['WebLoader\\Nette\\LoaderFactory']; + // } + // + // $init = $class->methods['initialize']; + // $init->addBody('if (!class_exists(?, ?)) class_alias(?, ?);', ['WebLoader\\LoaderFactory', false, 'WebLoader\\Nette\\LoaderFactory', 'WebLoader\\LoaderFactory']); + // } + + + public function install(Configurator $configurator): void + { + $self = $this; + $configurator->onCompile[] = function ($configurator, Compiler $compiler) use ($self): void { + $compiler->addExtension($self::EXTENSION_NAME, $self); + }; + } + + + /** + * @param array $filesConfig + * @param string $sourceDir + * @return array + * @throws FileNotFoundException + */ + private function findFiles(array $filesConfig, string $sourceDir): array + { + $normalizedFiles = []; + + /** @var array|string $file */ + foreach ($filesConfig as $file) { + // finder support + if (is_array($file) && isset($file['files']) && (isset($file['in']) || isset($file['from']))) { + $finder = Finder::findFiles($file['files']); + + if (isset($file['exclude'])) { + $finder->exclude($file['exclude']); + } + + if (isset($file['in'])) { + $finder->in(is_dir($file['in']) ? $file['in'] : $sourceDir . DIRECTORY_SEPARATOR . $file['in']); + } else { + $finder->from(is_dir($file['from']) ? $file['from'] : $sourceDir . DIRECTORY_SEPARATOR . $file['from']); + } + + $foundFilesList = []; + foreach ($finder as $foundFile) { + /** @var FileInfo $foundFile */ + $foundFilesList[] = $foundFile->getPathname(); + } + + natsort($foundFilesList); + + /** @var string $foundFilePathname */ + foreach ($foundFilesList as $foundFilePathname) { + $normalizedFiles[] = $foundFilePathname; + } + + } else { + if (is_string($file)) { + $this->checkFileExists($file, $sourceDir); + $normalizedFiles[] = $file; + } + } + } + + return $normalizedFiles; + } + + + protected function checkFileExists(string $file, string $sourceDir): void + { + if (!$this->fileExists($file)) { + $tmp = rtrim($sourceDir, '/\\') . DIRECTORY_SEPARATOR . $file; + if (!$this->fileExists($tmp)) { + throw new FileNotFoundException(sprintf("Neither '%s' or '%s' was found", $file, $tmp)); + } + } + } + + + /** + * Some servers seem to have problems under cron user with open_basedir restriction when using relative paths + */ + protected function fileExists(string $file): bool + { + $file = realpath($file); + + if ($file === false) { + $file = ''; + } + + return file_exists($file); + } + + + private function extractBatchesFromExtensions(): void + { + // Extension batches + /** @var array $batchProviders */ + $batchProviders = $this->compiler->getExtensions(IWebloaderAssetProvider::class); + + if (empty($batchProviders)) { + return; + } + + $schemaProcessor = new Processor; + + foreach ($batchProviders as $batchProvider) { + $assets = $batchProvider->getWebloaderAssets(); + $schemaProcessor->process($this->getConfigSchema(), $assets); + + foreach ($assets as $type => $batches) { + foreach ($batches as $name => $batch) { + $this->batchCollection->addBatch($type, $name, $batch); + } + } + } + } + + + /** + * @param array $config + * @throws BatchAlreadyExistsException + */ + private function extractNormalBatches(array $config): void + { + foreach (['css', 'js'] as $type) { + foreach ($config[$type] as $name => $batch) { + $this->batchCollection->addBatch($type, $name, $batch); + } + } + } +} diff --git a/WebLoader/Nette/JavaScriptLoader.php b/src/Nette/JavaScriptLoader.php similarity index 50% rename from WebLoader/Nette/JavaScriptLoader.php rename to src/Nette/JavaScriptLoader.php index a752f17..45ee64e 100644 --- a/WebLoader/Nette/JavaScriptLoader.php +++ b/src/Nette/JavaScriptLoader.php @@ -1,10 +1,12 @@ setAttribute('async', $this->getCompiler()->isAsync()); $el->setAttribute('defer', $this->getCompiler()->isDefer()); $el->setAttribute('nonce', $this->getCompiler()->getNonce()); - $el->setAttribute('src', $source); + $el->setAttribute('src', $this->getGeneratedFilePath($file)); + + return $el; + } + + + public function getInlineElement(File $file): Html + { + $el = Html::el('script'); + $el->setAttribute('nonce', $this->getCompiler()->getNonce()); + $el->setHtml(FileSystem::read($file->getPath())); return $el; } diff --git a/src/Nette/LoaderFactory.php b/src/Nette/LoaderFactory.php new file mode 100644 index 0000000..51ac554 --- /dev/null +++ b/src/Nette/LoaderFactory.php @@ -0,0 +1,70 @@ + $tempPaths */ + public function __construct( + private array $tempPaths, + private string $extensionName, + private IRequest $httpRequest, + private Container $diContainer + ) { + } + + + private function getCompiler(string $name, string $type): Compiler + { + /** @var Compiler $compiler */ + $compiler = $this->diContainer->getService( + $this->extensionName . + '.' . + $type . + ucfirst($name) . + 'Compiler' + ); + return $compiler; + } + + + public function createCssLoader(string $name, bool $appendLastModified = true): CssLoader + { + $compiler = $this->getCompiler($name, 'css'); + $this->modifyConvention($compiler->getOutputNamingConvention(), $name); + return new CssLoader($compiler, $this->formatTempPath($name, $compiler->isAbsoluteUrl()), $appendLastModified); + } + + + public function createJavaScriptLoader(string $name, bool $appendLastModified = true): JavaScriptLoader + { + $compiler = $this->getCompiler($name, 'js'); + $this->modifyConvention($compiler->getOutputNamingConvention(), $name); + return new JavaScriptLoader($compiler, $this->formatTempPath($name, $compiler->isAbsoluteUrl()), $appendLastModified); + } + + + private function formatTempPath(string $name, bool $absoluteUrl = false): string + { + $lName = strtolower($name); + $tempPath = $this->tempPaths[$lName] ?? Extension::DEFAULT_TEMP_PATH; + $method = $absoluteUrl ? 'getBaseUrl' : 'getBasePath'; + return rtrim($this->httpRequest->getUrl()->withoutUserInfo()->{$method}(), '/') . '/' . $tempPath; + } + + + private function modifyConvention(IOutputNamingConvention $convention, string $name): void + { + if ($convention instanceof DefaultOutputNamingConvention) { + $convention->setPrefix($name . '-'); + } + } +} diff --git a/src/Nette/SymfonyConsole/GenerateCommand.php b/src/Nette/SymfonyConsole/GenerateCommand.php new file mode 100644 index 0000000..e4050d0 --- /dev/null +++ b/src/Nette/SymfonyConsole/GenerateCommand.php @@ -0,0 +1,61 @@ +findByType(Compiler::class); + foreach ($compilers as $compilerName) { + $this->compilers[$compilerName] = $container->getService($compilerName); + } + } + + + protected function configure(): void + { + $this->addOption('force', 'f', InputOption::VALUE_NONE, 'Generate if not modified.'); + } + + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $noFiles = true; + foreach ($this->compilers as $compiler) { + $file = $compiler->generate(); + if (null !== $file) { + $output->writeln($file->getFileName()); + $noFiles = false; + } + } + + if ($noFiles) { + $output->writeln('No files generated.'); + return 1; + } + + return 0; + } +} diff --git a/src/Nette/WebLoader.php b/src/Nette/WebLoader.php new file mode 100755 index 0000000..6888af8 --- /dev/null +++ b/src/Nette/WebLoader.php @@ -0,0 +1,121 @@ +compiler; + } + + + public function setCompiler(Compiler $compiler): void + { + $this->compiler = $compiler; + } + + + public function getTempPath(): string + { + return $this->tempPath; + } + + + public function setTempPath(string $tempPath): void + { + $this->tempPath = $tempPath; + } + + + /** + * Get html element including generated content + */ + abstract public function getElement(File $file): Html; + + + abstract public function getInlineElement(File $file): Html; + + + public function setRenderMode(RenderMode $renderMode): void + { + $this->renderMode = $renderMode; + } + + + protected function getUrl(File $file): string + { + return $this->getGeneratedFilePath($file); + } + + + /** + * Generate compiled file(s) and render link(s) + */ + public function render(): void + { + $file = $this->compiler->generate(); + + if ($file === null) { + return; + } + + $output = match ($this->renderMode) { + RenderMode::URL => $this->getUrl($file), + RenderMode::LINK => $this->getElement($file), + RenderMode::INLINE => $this->getInlineElement($file), + }; + + echo $output, PHP_EOL; + } + + + public function renderInline(): void + { + $this->setRenderMode(RenderMode::INLINE); + $this->render(); + } + + + public function renderUrl(): void + { + $this->setRenderMode(renderMode::URL); + $this->render(); + } + + + protected function getGeneratedFilePath(File $file): string + { + $path = $this->tempPath . '/' . $file->getFileName(); + + if ($this->appendLastModified) { + $path .= '?' . $file->getLastModified(); + } + + return $path; + } +} diff --git a/src/Path.php b/src/Path.php new file mode 100755 index 0000000..1b9a650 --- /dev/null +++ b/src/Path.php @@ -0,0 +1,15 @@ +batchCollection = new BatchCollection(); + } + + + public function testAddGetBatches(): void + { + $this->batchCollection->addBatch('css', 'front.screen', []); + $this->batchCollection->addBatch('js', 'front.head', []); + + $expected = [ + 'css' => [ + 'front.screen' => [], + ], + 'js' => [ + 'front.head' => [], + ], + ]; + + $this->assertSame($expected, $this->batchCollection->getBatches()); + } + + + public function testAddBatchException(): void + { + $this->expectException(BatchAlreadyExistsException::class); + $this->batchCollection->addBatch('css', 'front.screen', []); + $this->batchCollection->addBatch('css', 'front.screen', []); + } +} diff --git a/tests/CompilerTest.php b/tests/CompilerTest.php old mode 100755 new mode 100644 index 03ab732..cf703e2 --- a/tests/CompilerTest.php +++ b/tests/CompilerTest.php @@ -3,10 +3,16 @@ namespace WebLoader\Test; +use Exception; use Mockery; use PHPUnit\Framework\TestCase; +use TypeError; use WebLoader\Compiler; +use WebLoader\Contract\IFileCollection; +use WebLoader\Contract\IOutputNamingConvention; use WebLoader\File; +use WebLoader\FileCollection; +use WebLoader\Exception\FileNotFoundException; /** * CompilerTest @@ -16,13 +22,12 @@ class CompilerTest extends TestCase { - /** @var \WebLoader\Compiler */ - private $object; + private Compiler $object; protected function setUp(): void { - $fileCollection = Mockery::mock('WebLoader\IFileCollection'); + $fileCollection = Mockery::mock(IFileCollection::class); $fileCollection->shouldReceive('getFiles')->andReturn([ __DIR__ . '/fixtures/a.txt', __DIR__ . '/fixtures/b.txt', @@ -34,7 +39,7 @@ protected function setUp(): void __DIR__ . '/fixtures/c.txt', ]); - $convention = Mockery::mock('WebLoader\IOutputNamingConvention'); + $convention = Mockery::mock(IOutputNamingConvention::class); $convention->shouldReceive('getFilename')->andReturnUsing(function ($files, $compiler) { return 'webloader-' . md5(join(',', $files)); }); @@ -47,49 +52,32 @@ protected function setUp(): void } + /** @return list */ private function getTempFiles(): array { - return glob(__DIR__ . '/temp/webloader-*'); - } - + $files = glob(__DIR__ . '/temp/webloader-*'); - public function testJoinFiles(): void - { - $this->assertTrue($this->object->getJoinFiles()); + if ($files === false) { + return []; + } - $ret = $this->object->generate(); - $this->assertEquals(1, count($ret), 'Multiple files are generated instead of join.'); - $this->assertEquals(1, count($this->getTempFiles()), 'Multiple files are generated instead of join.'); + return $files; } public function testEmptyFiles(): void { - $this->assertTrue($this->object->getJoinFiles()); - $this->object->setFileCollection(new \WebLoader\FileCollection()); + $this->object->setFileCollection(new FileCollection('')); $ret = $this->object->generate(); - $this->assertEquals(0, count($ret)); - $this->assertEquals(0, count($this->getTempFiles())); + $this->assertNull($ret); + $this->assertCount(0, $this->getTempFiles()); } - public function testNotJoinFiles(): void - { - $this->object->setJoinFiles(false); - $this->assertFalse($this->object->getJoinFiles()); - - $ret = $this->object->generate(); - $this->assertEquals(3, count($ret), 'Wrong file count generated.'); - $this->assertEquals(3, count($this->getTempFiles()), 'Wrong file count generated.'); - } - - - /** - * @expectedException \WebLoader\FileNotFoundException - */ public function testSetOutDir(): void { + $this->expectException(FileNotFoundException::class); $this->object->setOutputDir('blablabla'); } @@ -112,14 +100,11 @@ public function testGeneratingAndFilters(): void $expectedContent = '-' . PHP_EOL . 'a:cba,' . PHP_EOL . 'b:fed,' . PHP_EOL . 'c:ihg,-' . PHP_EOL . 'a:cba,' . PHP_EOL . 'b:fed,' . PHP_EOL . 'c:ihg,'; - /** - * @var $files File[] - */ - $files = $this->object->generate(); + $file = $this->generateFile(); - $this->assertTrue(is_numeric($files[0]->getLastModified()) && $files[0]->getLastModified() > 0, 'Generate does not provide last modified timestamp correctly.'); + $this->assertTrue($file->getLastModified() && $file->getLastModified() > 0, 'Generate does not provide last modified timestamp correctly.'); - $content = file_get_contents($this->object->getOutputDir() . '/' . $files[0]->getFile()); + $content = file_get_contents($this->object->getOutputDir() . '/' . $file->getFileName()); $this->assertEquals($expectedContent, $content); } @@ -127,19 +112,16 @@ public function testGeneratingAndFilters(): void public function testGenerateReturnsSourceFilePaths(): void { - /** - * @var $res File[] - */ - $res = $this->object->generate(); - $this->assertInternalType('array', $res[0]->getSourceFiles()); - $this->assertCount(3, $res[0]->getSourceFiles()); - $this->assertFileExists($res[0]->getSourceFiles()[0]); + $file = $this->generateFile(); + + $this->assertCount(3, $file->getSourceFiles()); + $this->assertFileExists($file->getSourceFiles()[0]); } public function testFilters(): void { - $filter = function ($code, \WebLoader\Compiler $loader) { + $filter = function ($code, Compiler $loader) { return $code . $code; }; $this->object->addFilter($filter); @@ -150,7 +132,7 @@ public function testFilters(): void public function testFileFilters(): void { - $filter = function ($code, \WebLoader\Compiler $loader, $file = null) { + $filter = function ($code, Compiler $loader, $file = null) { return $code . $code; }; $this->object->addFileFilter($filter); @@ -159,20 +141,28 @@ public function testFileFilters(): void } - /** - * @expectedException \TypeError - */ public function testNonCallableFilter(): void { + $this->expectException(TypeError::class); $this->object->addFilter(4); } - /** - * @expectedException \TypeError - */ public function testNonCallableFileFilter(): void { + $this->expectException(TypeError::class); $this->object->addFileFilter(4); } + + + private function generateFile(): File + { + $file = $this->object->generate(); + + if ($file === null) { + throw new Exception('Should not be empty'); + } + + return $file; + } } diff --git a/tests/DefaultOutputNamingConventionTest.php b/tests/DefaultOutputNamingConventionTest.php old mode 100755 new mode 100644 index df285a1..3e7fee5 --- a/tests/DefaultOutputNamingConventionTest.php +++ b/tests/DefaultOutputNamingConventionTest.php @@ -3,7 +3,9 @@ namespace WebLoader\Test; +use Mockery; use PHPUnit\Framework\TestCase; +use WebLoader\Compiler; use WebLoader\DefaultOutputNamingConvention; /** @@ -14,16 +16,14 @@ class DefaultOutputNamingConventionTest extends TestCase { - /** @var DefaultOutputNamingConvention */ - private $object; - - private $compiler; + private DefaultOutputNamingConvention $object; + protected Compiler $compiler; protected function setUp(): void { $this->object = new DefaultOutputNamingConvention(); - $this->compiler = \Mockery::mock('Webloader\Compiler'); + $this->compiler = Mockery::mock(Compiler::class); } @@ -35,7 +35,7 @@ public function testMultipleFiles(): void ]; $name = $this->object->getFilename($files, $this->compiler); - $this->assertRegExp('/^[0-9a-f]{12}$/', $name); + $this->assertMatchesRegularExpression('/^[0-9a-f]{12}$/', $name); // another hash $files[] = __DIR__ . DIRECTORY_SEPARATOR . 'fixtures' . DIRECTORY_SEPARATOR . 'c.txt'; @@ -51,7 +51,7 @@ public function testOneFile(): void ]; $name = $this->object->getFilename($files, $this->compiler); - $this->assertRegExp('/^[0-9a-f]{12}$/', $name); + $this->assertMatchesRegularExpression('/^[0-9a-f]{12}$/', $name); } @@ -62,7 +62,7 @@ public function testCssConvention(): void ]; $name = DefaultOutputNamingConvention::createCssConvention()->getFilename($files, $this->compiler); - $this->assertRegExp('/^[0-9a-f]{12}.css$/', $name); + $this->assertMatchesRegularExpression('/^[0-9a-f]{12}.css$/', $name); } @@ -73,6 +73,6 @@ public function testJsConvention(): void ]; $name = DefaultOutputNamingConvention::createJsConvention()->getFilename($files, $this->compiler); - $this->assertRegExp('/^[0-9a-f]{12}.js$/', $name); + $this->assertMatchesRegularExpression('/^[0-9a-f]{12}.js$/', $name); } } diff --git a/tests/FileCollectionTest.php b/tests/FileCollectionTest.php index c5af843..2f8496f 100755 --- a/tests/FileCollectionTest.php +++ b/tests/FileCollectionTest.php @@ -3,8 +3,11 @@ namespace WebLoader\Test; +use ArrayIterator; use PHPUnit\Framework\TestCase; +use SplFileInfo; use WebLoader\FileCollection; +use WebLoader\Exception\FileNotFoundException; /** * FileCollection test @@ -14,8 +17,7 @@ class FileCollectionTest extends TestCase { - /** @var FileCollection */ - private $object; + private FileCollection $object; protected function setUp(): void @@ -39,16 +41,14 @@ public function testAddGetFiles(): void } - /** - * @expectedException \Webloader\FileNotFoundException - */ - public function testAddNonExistingFile() + public function testAddNonExistingFile(): void { + $this->expectException(FileNotFoundException::class); $this->object->addFile('sdfsdg.txt'); } - public function testRemoveFile() + public function testRemoveFile(): void { $this->object->addFile(__DIR__ . '/fixtures/a.txt'); $this->object->addFile(__DIR__ . '/fixtures/b.txt'); @@ -63,7 +63,7 @@ public function testRemoveFile() } - public function testCannonicalizePath() + public function testCannonicalizePath(): void { $abs = __DIR__ . '/./fixtures/a.txt'; $rel = 'a.txt'; @@ -75,41 +75,23 @@ public function testCannonicalizePath() try { $this->object->cannonicalizePath('nesdagf'); $this->fail('Exception was not thrown.'); - } catch (\WebLoader\FileNotFoundException $e) { + } catch (FileNotFoundException $e) { } } - public function testClear() + public function testClear(): void { $this->object->addFile('a.txt'); - $this->object->addRemoteFile('http://jquery.com/jquery.js'); $this->object->addWatchFile('b.txt'); $this->object->clear(); $this->assertEquals([], $this->object->getFiles()); - $this->assertEquals([], $this->object->getRemoteFiles()); $this->assertEquals([], $this->object->getWatchFiles()); } - public function testRemoteFiles() - { - $this->object->addRemoteFile('http://jquery.com/jquery.js'); - $this->object->addRemoteFiles([ - 'http://jquery.com/jquery.js', - 'http://google.com/angular.js', - ]); - - $expected = [ - 'http://jquery.com/jquery.js', - 'http://google.com/angular.js', - ]; - $this->assertEquals($expected, $this->object->getRemoteFiles()); - } - - - public function testWatchFiles() + public function testWatchFiles(): void { $this->object->addWatchFile(__DIR__ . '/fixtures/a.txt'); $this->object->addWatchFile(__DIR__ . '/fixtures/b.txt'); @@ -123,28 +105,25 @@ public function testWatchFiles() } - public function testTraversableFiles() + public function testTraversableFiles(): void { - $this->object->addFiles(new \ArrayIterator(['a.txt'])); - $this->assertEquals(1, count($this->object->getFiles())); + $this->object->addFiles(new ArrayIterator(['a.txt'])); + $this->assertCount(1, $this->object->getFiles()); } - public function testTraversableRemoteFiles() + public function testSplFileInfo(): void { - $this->object->addRemoteFiles(new \ArrayIterator(['http://jquery.com/jquery.js'])); - $this->assertEquals(1, count($this->object->getRemoteFiles())); + $this->object->addFile(new SplFileInfo(__DIR__ . '/fixtures/a.txt')); + $this->assertCount(1, $this->object->getFiles()); } - public function testSplFileInfo() - { - $this->object->addFile(new \SplFileInfo(__DIR__ . '/fixtures/a.txt')); - $this->assertEquals(1, count($this->object->getFiles())); - } - - - private function assertEqualPaths($expected, $actual) + /** + * @param mixed $expected + * @param mixed $actual + */ + private function assertEqualPaths($expected, $actual): void { $actual = (array) $actual; foreach ((array) $expected as $key => $path) { diff --git a/tests/Filter/CssMinFilterTest.php b/tests/Filter/CssMinFilterTest.php new file mode 100755 index 0000000..aeb8b9f --- /dev/null +++ b/tests/Filter/CssMinFilterTest.php @@ -0,0 +1,38 @@ +filter = new CssMinFilter(); + + $files = new FileCollection(__DIR__ . '/../fixtures'); + @mkdir($outputDir = __DIR__ . '/../temp/'); + $this->compiler = new Compiler($files, new DefaultOutputNamingConvention(), $outputDir); + } + + + public function testMinify(): void + { + $file = __DIR__ . '/../fixtures/cssmin.css'; + $minified = $this->filter->__invoke( + (string) file_get_contents($file), + $this->compiler, + $file + ); + $this->assertSame(file_get_contents(__DIR__ . '/../fixtures/cssmin.css.expected'), $minified); + } +} diff --git a/tests/Filter/CssUrlsFilterTest.php b/tests/Filter/CssUrlsFilterTest.php index 9113527..8d1b6d7 100755 --- a/tests/Filter/CssUrlsFilterTest.php +++ b/tests/Filter/CssUrlsFilterTest.php @@ -9,8 +9,7 @@ class CssUrlsFilterTest extends TestCase { - /** @var CssUrlsFilter */ - private $object; + private CssUrlsFilter $object; protected function setUp(): void diff --git a/tests/Filter/JsMinFilterTest.php b/tests/Filter/JsMinFilterTest.php new file mode 100755 index 0000000..4bdb734 --- /dev/null +++ b/tests/Filter/JsMinFilterTest.php @@ -0,0 +1,38 @@ +filter = new JsMinFilter(); + + $files = new FileCollection(__DIR__ . '/../fixtures'); + @mkdir($outputDir = __DIR__ . '/../temp/'); + $this->compiler = new Compiler($files, new DefaultOutputNamingConvention(), $outputDir); + } + + + public function testMinify(): void + { + $file = __DIR__ . '/../fixtures/jsmin.js'; + $minified = $this->filter->__invoke( + (string) file_get_contents($file), + $this->compiler, + $file + ); + $this->assertSame(file_get_contents(__DIR__ . '/../fixtures/jsmin.js.expected'), $minified); + } +} diff --git a/tests/Filter/LessFilterTest.php b/tests/Filter/LessFilterTest.php index e0d9c0d..20c9079 100755 --- a/tests/Filter/LessFilterTest.php +++ b/tests/Filter/LessFilterTest.php @@ -11,16 +11,13 @@ class LessFilterTest extends TestCase { - /** @var LessFilter */ - private $filter; - - /** @var Compiler */ - private $compiler; + private LessFilter $filter; + private Compiler $compiler; protected function setUp(): void { - $this->filter = new LessFilter(new \lessc()); + $this->filter = new LessFilter(); $files = new FileCollection(__DIR__ . '/../fixtures'); @mkdir($outputDir = __DIR__ . '/../temp/'); @@ -31,7 +28,12 @@ protected function setUp(): void public function testReplace(): void { $file = __DIR__ . '/../fixtures/style.less'; - $less = $this->filter->__invoke(file_get_contents($file), $this->compiler, $file); - $this->assertSame(file_get_contents(__DIR__ . '/../fixtures/style.less.expected'), $less); + $minified = $this->filter->__invoke( + (string) file_get_contents($file), + $this->compiler, + $file + ); + + $this->assertSame(file_get_contents(__DIR__ . '/../fixtures/style.less.expected'), $minified); } } diff --git a/tests/Filter/PHPCoffeeScriptFilterTest.php b/tests/Filter/PHPCoffeeScriptFilterTest.php deleted file mode 100755 index 3114ccf..0000000 --- a/tests/Filter/PHPCoffeeScriptFilterTest.php +++ /dev/null @@ -1,51 +0,0 @@ -object = new PHPCoffeeScriptFilter(); - } - - - public function testSimpleLoadAndParse(): void - { - if (!class_exists('CoffeeScript\Compiler')) { - $this->markTestSkipped('Missing CoffeeScript compiler.'); - } - - $compiler = new PHPCoffeeScriptFilter(); - $coffee = $compiler->compileCoffee('number = -42 if opposite', null); - - $version = COFFEESCRIPT_VERSION; - $expected = <<assertEquals($expected, $coffee); - } -} diff --git a/tests/Filter/ScssFilterTest.php b/tests/Filter/ScssFilterTest.php index 1dd4904..5e643b1 100755 --- a/tests/Filter/ScssFilterTest.php +++ b/tests/Filter/ScssFilterTest.php @@ -4,6 +4,7 @@ namespace WebLoader\Test\Filter; use PHPUnit\Framework\TestCase; +use ScssPhp\ScssPhp\Compiler as ScssCompiler; use WebLoader\Compiler; use WebLoader\DefaultOutputNamingConvention; use WebLoader\FileCollection; @@ -12,16 +13,13 @@ class ScssFilterTest extends TestCase { - /** @var ScssFilter */ - private $filter; - - /** @var Compiler */ - private $compiler; + private ScssFilter $filter; + private Compiler $compiler; protected function setUp(): void { - $this->filter = new ScssFilter(new \Leafo\ScssPhp\Compiler()); + $this->filter = new ScssFilter(new ScssCompiler()); $files = new FileCollection(__DIR__ . '/../fixtures'); @mkdir($outputDir = __DIR__ . '/../temp/'); @@ -32,7 +30,11 @@ protected function setUp(): void public function testReplace(): void { $file = __DIR__ . '/../fixtures/style.scss'; - $less = $this->filter->__invoke(file_get_contents($file), $this->compiler, $file); + $less = $this->filter->__invoke( + (string) file_get_contents($file), + $this->compiler, + $file + ); $this->assertSame(file_get_contents(__DIR__ . '/../fixtures/style.scss.expected'), $less); } @@ -41,9 +43,9 @@ public function testImportAbsolutePath(): void { $file = __DIR__ . '/../fixtures/styleAbsolute.scss'; $filter = new VariablesFilter([ - 'fixturesAbsolutePath' => realpath(__DIR__ . '/../fixtures'), + 'fixturesAbsolutePath' => (string) realpath(__DIR__ . '/../fixtures'), ]); - $code = file_get_contents($file); + $code = (string) file_get_contents($file); $filtered = $filter($code); $less = $this->filter->__invoke($filtered, $this->compiler, $file); $this->assertSame(file_get_contents(__DIR__ . '/../fixtures/styleAbsolute.scss.expected'), $less); diff --git a/tests/Filter/VariablesFilterTest.php b/tests/Filter/VariablesFilterTest.php old mode 100755 new mode 100644 index 3c3b534..9e720ee --- a/tests/Filter/VariablesFilterTest.php +++ b/tests/Filter/VariablesFilterTest.php @@ -9,8 +9,7 @@ class VariablesFilterTest extends TestCase { - /** @var VariablesFilter */ - private $object; + private VariablesFilter $object; protected function setUp(): void diff --git a/tests/Nette/ExtensionTest.php b/tests/Nette/ExtensionTest.php index 3fbb4c6..2856126 100755 --- a/tests/Nette/ExtensionTest.php +++ b/tests/Nette/ExtensionTest.php @@ -3,43 +3,61 @@ namespace WebLoader\Test\Nette; -use Nette\Configurator; +use Nette\Bootstrap\Configurator; use Nette\DI\Compiler; use Nette\DI\Container; use Nette\Utils\Finder; use PHPUnit\Framework\TestCase; +use WebLoader\Compiler as WebloaderCompiler; use WebLoader\Nette\Extension; use WebLoader\Path; - class ExtensionTest extends TestCase { + private Container $container; + private string $appDir; + private string $wwwDir; + private string $tempDir; + private string $fixturesDir; + private bool $debugMode; + - /** @var Container */ - private $container; + protected function setUp(): void + { + $this->appDir = __DIR__; + $this->wwwDir = $this->appDir . '/..'; + $this->tempDir = $this->wwwDir . '/../temp'; + $this->fixturesDir = $this->appDir . '/../fixtures'; + $this->debugMode = false; + } - private function prepareContainer($configFiles): void + /** @param list $configFiles */ + private function prepareContainer(array $configFiles): void { - $tempDir = __DIR__ . '/../temp'; - foreach (Finder::findFiles('*')->exclude('.gitignore')->from($tempDir . '/cache') as $file) { + $finder = Finder::findFiles('*') + ->exclude('.gitignore') + ->from($this->tempDir . '/cache'); + + foreach ($finder as $file) { unlink((string) $file); } $configurator = new Configurator(); - $configurator->setTempDirectory($tempDir); + $configurator->setTempDirectory($this->tempDir); + /** @var string $file */ foreach ($configFiles as $file) { $configurator->addConfig($file); } $configurator->addParameters([ - 'wwwDir' => __DIR__ . '/..', - 'fixturesDir' => __DIR__ . '/../fixtures', - 'tempDir' => $tempDir, + 'wwwDir' => $this->wwwDir, + 'fixturesDir' => $this->fixturesDir, + 'tempDir' => $this->tempDir, ]); - $extension = new Extension(__DIR__ . '/..', $configurator->isDebugMode()); + $extension = new Extension($this->appDir, $this->wwwDir, $this->debugMode); $extension->install($configurator); $this->container = @$configurator->createContainer(); // sends header X-Powered-By, ... @@ -48,55 +66,26 @@ private function prepareContainer($configFiles): void public function testJsCompilerService(): void { - $this->prepareContainer([__DIR__ . '/../fixtures/extension.neon']); + $this->prepareContainer([$this->fixturesDir . '/extension.neon']); $this->assertInstanceOf('WebLoader\Compiler', $this->container->getService('webloader.jsDefaultCompiler')); } public function testExcludeFiles(): void { - $this->prepareContainer([__DIR__ . '/../fixtures/extension.neon']); + $this->prepareContainer([$this->fixturesDir . '/extension.neon']); $files = $this->container->getService('webloader.jsExcludeCompiler')->getFileCollection()->getFiles(); - $this->assertTrue(in_array(Path::normalize(__DIR__ . '/../fixtures/a.txt'), $files, true)); - $this->assertFalse(in_array(Path::normalize(__DIR__ . '/../fixtures/dir/one.js'), $files, true)); - } - - - public function testJoinFilesOn(): void - { - $this->prepareContainer([ - __DIR__ . '/../fixtures/extension.neon', - __DIR__ . '/../fixtures/extensionJoinFilesTrue.neon', - ]); - $this->assertTrue($this->container->getService('webloader.jsDefaultCompiler')->getJoinFiles()); - } - - - public function testJoinFilesOff(): void - { - $this->prepareContainer([ - __DIR__ . '/../fixtures/extension.neon', - __DIR__ . '/../fixtures/extensionJoinFilesFalse.neon', - ]); - $this->assertFalse($this->container->getService('webloader.jsDefaultCompiler')->getJoinFiles()); - } - - - public function testJoinFilesOffInOneService(): void - { - $this->prepareContainer([ - __DIR__ . '/../fixtures/extension.neon', - ]); - $this->assertFalse($this->container->getService('webloader.cssJoinOffCompiler')->getJoinFiles()); + $this->assertTrue(in_array(Path::normalize($this->fixturesDir . '/a.txt'), $files, true)); + $this->assertFalse(in_array(Path::normalize($this->fixturesDir . '/dir/one.js'), $files, true)); } public function testAsyncOn(): void { $this->prepareContainer([ - __DIR__ . '/../fixtures/extension.neon', - __DIR__ . '/../fixtures/extensionAsyncTrue.neon', + $this->fixturesDir . '/extension.neon', + $this->fixturesDir . '/extensionAsyncTrue.neon', ]); $this->assertTrue($this->container->getService('webloader.jsDefaultCompiler')->isAsync()); } @@ -105,8 +94,8 @@ public function testAsyncOn(): void public function testAsyncOff(): void { $this->prepareContainer([ - __DIR__ . '/../fixtures/extension.neon', - __DIR__ . '/../fixtures/extensionAsyncFalse.neon', + $this->fixturesDir . '/extension.neon', + $this->fixturesDir . '/extensionAsyncFalse.neon', ]); $this->assertFalse($this->container->getService('webloader.jsDefaultCompiler')->isAsync()); } @@ -115,8 +104,8 @@ public function testAsyncOff(): void public function testDeferOn(): void { $this->prepareContainer([ - __DIR__ . '/../fixtures/extension.neon', - __DIR__ . '/../fixtures/extensionDeferTrue.neon', + $this->fixturesDir . '/extension.neon', + $this->fixturesDir . '/extensionDeferTrue.neon', ]); $this->assertTrue($this->container->getService('webloader.jsDefaultCompiler')->isDefer()); } @@ -125,8 +114,8 @@ public function testDeferOn(): void public function testDeferOff(): void { $this->prepareContainer([ - __DIR__ . '/../fixtures/extension.neon', - __DIR__ . '/../fixtures/extensionDeferFalse.neon', + $this->fixturesDir . '/extension.neon', + $this->fixturesDir . '/extensionDeferFalse.neon', ]); $this->assertFalse($this->container->getService('webloader.jsDefaultCompiler')->isDefer()); } @@ -135,8 +124,8 @@ public function testDeferOff(): void public function testAbsoluteUrlOn(): void { $this->prepareContainer([ - __DIR__ . '/../fixtures/extension.neon', - __DIR__ . '/../fixtures/extensionAbsoluteUrlTrue.neon', + $this->fixturesDir . '/extension.neon', + $this->fixturesDir . '/extensionAbsoluteUrlTrue.neon', ]); $this->assertTrue($this->container->getService('webloader.jsDefaultCompiler')->isAbsoluteUrl()); } @@ -145,8 +134,8 @@ public function testAbsoluteUrlOn(): void public function testAbsoluteUrlOff(): void { $this->prepareContainer([ - __DIR__ . '/../fixtures/extension.neon', - __DIR__ . '/../fixtures/extensionAbsoluteUrlFalse.neon', + $this->fixturesDir . '/extension.neon', + $this->fixturesDir . '/extensionAbsoluteUrlFalse.neon', ]); $this->assertFalse($this->container->getService('webloader.jsDefaultCompiler')->isAbsoluteUrl()); } @@ -155,8 +144,8 @@ public function testAbsoluteUrlOff(): void public function testNonceSet(): void { $this->prepareContainer([ - __DIR__ . '/../fixtures/extension.neon', - __DIR__ . '/../fixtures/extensionNonce.neon', + $this->fixturesDir . '/extension.neon', + $this->fixturesDir . '/extensionNonce.neon', ]); $this->assertEquals('rAnd0m123', $this->container->getService('webloader.jsDefaultCompiler')->getNonce()); } @@ -165,7 +154,7 @@ public function testNonceSet(): void public function testNonceNotSet(): void { $this->prepareContainer([ - __DIR__ . '/../fixtures/extension.neon', + $this->fixturesDir . '/extension.neon', ]); $this->assertNull($this->container->getService('webloader.jsDefaultCompiler')->getNonce()); } @@ -173,20 +162,25 @@ public function testNonceNotSet(): void public function testExtensionName(): void { - $tempDir = __DIR__ . '/../temp'; $class = 'ExtensionNameServiceContainer'; $configurator = new Configurator(); - $configurator->setTempDirectory($tempDir); + $configurator->setTempDirectory($this->tempDir); $configurator->addParameters(['container' => ['class' => $class]]); $configurator->onCompile[] = function (Configurator $configurator, Compiler $compiler) { - $extension = new Extension(__DIR__ . '/..', $configurator->isDebugMode()); + $extension = new Extension($this->appDir, $this->wwwDir, $this->debugMode); $compiler->addExtension('Foo', $extension); }; - $configurator->addConfig(__DIR__ . '/../fixtures/extensionName.neon'); + $configurator->addConfig($this->fixturesDir . '/extensionName.neon'); $container = $configurator->createContainer(); - $this->assertInstanceOf('WebLoader\Compiler', $container->getService('Foo.cssDefaultCompiler')); - $this->assertInstanceOf('WebLoader\Compiler', $container->getService('Foo.jsDefaultCompiler')); + $this->assertInstanceOf( + WebloaderCompiler::class, + $container->getService('Foo.cssDefaultCompiler') + ); + $this->assertInstanceOf( + WebloaderCompiler::class, + $container->getService('Foo.jsDefaultCompiler') + ); } } diff --git a/tests/Path/PathTest.php b/tests/Path/PathTest.php index b8f25dd..1fabe27 100755 --- a/tests/Path/PathTest.php +++ b/tests/Path/PathTest.php @@ -13,4 +13,11 @@ public function testNormalize(): void $normalized = Path::normalize('/path/to//project//that/contains/0/in/it'); $this->assertEquals('/path/to/project/that/contains/0/in/it', $normalized); } + + + public function testDirectoryJump(): void + { + $normalized = Path::normalize('/path/to/my/project/../../wrong/path/../../correct/one/'); + $this->assertEquals('/path/to/correct/one/', $normalized); + } } diff --git a/tests/fixtures/cssmin.css b/tests/fixtures/cssmin.css new file mode 100755 index 0000000..6ad0010 --- /dev/null +++ b/tests/fixtures/cssmin.css @@ -0,0 +1,57 @@ +.clearFix { + display: block; + zoom: 1; +} +.clearFix:after { + content: " "; + display: block; + font-size: 0; + height: 0; + clear: both; + visibility: hidden; +} +div.banners { + display: block; + zoom: 1; + padding: 0 0 20px; + margin: 0 10px; + border-bottom: #f4f4f4 1px solid; +} +div.banners:after { + content: " "; + display: block; + font-size: 0; + height: 0; + clear: both; + visibility: hidden; +} +div.banners > div { + float: left; + width: 610px; + height: 194px; + background: #f9f2e8; + margin: 20px 0 0 20px; + position: relative; +} +div.banners > div h3 { + width: auto; + color: #be2025; + font-weight: 600; + padding: 10px 20px; + margin: 10px 10px 0 10px; + text-shadow: 0 2px 0 rgba(0, 0, 0, 0.3); + display: inline-block; + font-size: 24px; +} +div.banners > div h3, +div.banners > div p { + color: #ffffff; +} +div.banners > div p { + font-size: 13px; + max-width: 360px; + padding: 10px; +} +div.banners > div p strong { + font-weight: 600; +} diff --git a/tests/fixtures/cssmin.css.expected b/tests/fixtures/cssmin.css.expected new file mode 100755 index 0000000..10ae4dd --- /dev/null +++ b/tests/fixtures/cssmin.css.expected @@ -0,0 +1 @@ +.clearFix{display:block;zoom:1}.clearFix:after{content:" ";display:block;font-size:0;height:0;clear:both;visibility:hidden}div.banners{display:block;zoom:1;padding:0 0 20px;margin:0 10px;border-bottom:#f4f4f4 1px solid}div.banners:after{content:" ";display:block;font-size:0;height:0;clear:both;visibility:hidden}div.banners>div{float:left;width:610px;height:194px;background:#f9f2e8;margin:20px 0 0 20px;position:relative}div.banners>div h3{width:auto;color:#be2025;font-weight:600;padding:10px 20px;margin:10px 10px 0;text-shadow:0 2px 0 rgba(0,0,0,.3);display:inline-block;font-size:24px}div.banners>div h3,div.banners>div p{color:#fff}div.banners>div p{font-size:13px;max-width:360px;padding:10px}div.banners>div p strong{font-weight:600} \ No newline at end of file diff --git a/tests/fixtures/extension.neon b/tests/fixtures/extension.neon index d8ac1d8..b71f33c 100755 --- a/tests/fixtures/extension.neon +++ b/tests/fixtures/extension.neon @@ -1,6 +1,3 @@ -parameters: - cssJoinFiles: false - http: frames: yes @@ -20,16 +17,9 @@ webloader: files: - style.css - {files: ["*.css"], from: %fixturesDir%/dir} - joinOff: - joinFiles: %cssJoinFiles% - files: - - style.css js: default: - remoteFiles: - - http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js - - http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js files: - %fixturesDir%/dir/one.js - dir/two.js @@ -37,4 +27,4 @@ webloader: - @variablesFilter exclude: files: - - {files: ["*"], from: %fixturesDir%, exclude: [dir/*]} \ No newline at end of file + - {files: ["*"], from: %fixturesDir%, exclude: [dir/*]} diff --git a/tests/fixtures/extensionJoinFilesFalse.neon b/tests/fixtures/extensionJoinFilesFalse.neon deleted file mode 100644 index 8a3f58d..0000000 --- a/tests/fixtures/extensionJoinFilesFalse.neon +++ /dev/null @@ -1,3 +0,0 @@ -webloader: - jsDefaults: - joinFiles: false \ No newline at end of file diff --git a/tests/fixtures/extensionJoinFilesTrue.neon b/tests/fixtures/extensionJoinFilesTrue.neon deleted file mode 100644 index 1523860..0000000 --- a/tests/fixtures/extensionJoinFilesTrue.neon +++ /dev/null @@ -1,3 +0,0 @@ -webloader: - jsDefaults: - joinFiles: true \ No newline at end of file diff --git a/tests/fixtures/jsmin.js b/tests/fixtures/jsmin.js new file mode 100755 index 0000000..2dd0288 --- /dev/null +++ b/tests/fixtures/jsmin.js @@ -0,0 +1,9 @@ +;(function() { + window.alert('Hello World!'); + + if (true === false) { + window.alert('Paradox'); + } else { + window.alert('All is well.'); + } +})(); diff --git a/tests/fixtures/jsmin.js.expected b/tests/fixtures/jsmin.js.expected new file mode 100755 index 0000000..238dd79 --- /dev/null +++ b/tests/fixtures/jsmin.js.expected @@ -0,0 +1 @@ +;(function(){window.alert('Hello World!');if(true===false){window.alert('Paradox');}else{window.alert('All is well.');}})(); \ No newline at end of file diff --git a/tests/fixtures/style.scss b/tests/fixtures/style.scss index 2e98946..2334920 100644 --- a/tests/fixtures/style.scss +++ b/tests/fixtures/style.scss @@ -1,19 +1,18 @@ - @import 'style2.scss'; .navigation { - ul { - line-height: 20px; - color: blue; - a { - color: red; - } - } + ul { + line-height: 20px; + color: blue; + + a { + color: red; + } + } } .footer { - .copyright { - color: silver; - } + .copyright { + color: silver; + } } - diff --git a/tests/fixtures/style.scss.expected b/tests/fixtures/style.scss.expected index d4249e7..fe3ebaf 100644 --- a/tests/fixtures/style.scss.expected +++ b/tests/fixtures/style.scss.expected @@ -1,12 +1,16 @@ .clearFix { display: block; - zoom: 1; } + zoom: 1; +} .navigation ul { line-height: 20px; - color: blue; } - .navigation ul a { - color: red; } + color: blue; +} +.navigation ul a { + color: red; +} .footer .copyright { - color: silver; } + color: silver; +} \ No newline at end of file diff --git a/tests/fixtures/style2.scss b/tests/fixtures/style2.scss index 1155d28..8de238e 100644 --- a/tests/fixtures/style2.scss +++ b/tests/fixtures/style2.scss @@ -1,4 +1,4 @@ .clearFix { - display: block; - zoom: 1; + display: block; + zoom: 1; } diff --git a/tests/fixtures/styleAbsolute.scss b/tests/fixtures/styleAbsolute.scss index e4ed002..2958f1d 100644 --- a/tests/fixtures/styleAbsolute.scss +++ b/tests/fixtures/styleAbsolute.scss @@ -1,19 +1,19 @@ - @import '{{$fixturesAbsolutePath}}/style2.scss'; .navigation { - ul { - line-height: 20px; - color: blue; - a { - color: red; - } - } + ul { + line-height: 20px; + color: blue; + + a { + color: red; + } + } } .footer { - .copyright { - color: silver; - } + .copyright { + color: silver; + } } diff --git a/tests/fixtures/styleAbsolute.scss.expected b/tests/fixtures/styleAbsolute.scss.expected index d4249e7..fe3ebaf 100644 --- a/tests/fixtures/styleAbsolute.scss.expected +++ b/tests/fixtures/styleAbsolute.scss.expected @@ -1,12 +1,16 @@ .clearFix { display: block; - zoom: 1; } + zoom: 1; +} .navigation ul { line-height: 20px; - color: blue; } - .navigation ul a { - color: red; } + color: blue; +} +.navigation ul a { + color: red; +} .footer .copyright { - color: silver; } + color: silver; +} \ No newline at end of file diff --git a/tests/phpunit.xml b/tests/phpunit.xml index 9e655c9..0cfdb62 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -1,12 +1,3 @@ - + +