├── .gitignore ├── .travis.yml ├── README.md ├── WebLoader ├── Compiler.php ├── DefaultOutputNamingConvention.php ├── FileCollection.php ├── FileNotFoundException.php ├── Filter │ ├── CoffeeScriptFilter.php │ ├── CssUrlsFilter.php │ ├── LessBinFilter.php │ ├── LessFilter.php │ ├── PHPCoffeeScriptFilter.php │ ├── Process.php │ ├── ScssFilter.php │ ├── StylusFilter.php │ ├── TypeScriptFilter.php │ └── VariablesFilter.php ├── IFileCollection.php ├── IOutputNamingConvention.php ├── InvalidArgumentException.php ├── Nette │ ├── CompilationException.php │ ├── CssLoader.php │ ├── CssUrlFilter.php │ ├── Diagnostics │ │ ├── Panel.php │ │ └── panel.latte │ ├── Extension.php │ ├── JavaScriptLoader.php │ ├── LoaderFactory.php │ └── WebLoader.php ├── Path.php └── WebLoaderException.php ├── composer.json └── tests ├── CompilerTest.php ├── DefaultOutputNamingConventionTest.php ├── FileCollectionTest.php ├── Filter ├── CssUrlsFilterTest.php ├── LessFilterTest.php ├── PHPCoffeeScriptFilterTest.php ├── ScssFilterTest.php └── VariablesFilterTest.php ├── Nette └── ExtensionTest.php ├── Path └── PathTest.php ├── bootstrap.php ├── fixtures ├── a.txt ├── b.txt ├── c.txt ├── dir │ ├── one.css │ ├── one.js │ ├── two.css │ └── two.js ├── extension.neon ├── extensionJoinFilesFalse.neon ├── extensionJoinFilesTrue.neon ├── extensionName.neon ├── style.css ├── style.less ├── style.less.expected ├── style.scss ├── style.scss.expected ├── style2.less ├── style2.scss ├── styleAbsolute.scss └── styleAbsolute.scss.expected ├── phpunit.xml ├── temp ├── .gitignore └── cache │ └── .gitignore └── travis ├── composer-nette-2.3.json └── prepare-composer.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | composer.lock -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | env: 4 | - NETTE=default # 2.3 dev 5 | - NETTE=nette-2.3 6 | 7 | php: 8 | - 5.4 9 | - 5.5 10 | - 5.6 11 | - 7.0 12 | - hhvm 13 | 14 | matrix: 15 | allow_failures: 16 | - php: 7.0 17 | - php: hhvm 18 | 19 | before_script: 20 | - php tests/travis/prepare-composer.php 21 | - composer self-update 22 | - composer install --no-interaction --prefer-source 23 | - vendor/bin/parallel-lint -e php --exclude vendor . 24 | 25 | script: vendor/bin/phpunit --configuration tests/phpunit.xml tests 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | WebLoader [![Build Status](https://secure.travis-ci.org/janmarek/WebLoader.png?branch=master)](http://travis-ci.org/janmarek/WebLoader) 2 | ======================= 3 | 4 | Component for CSS and JS files loading 5 | 6 | Author: Jan Marek 7 | Licence: MIT 8 | 9 | Example 10 | ------- 11 | 12 | Control factory in Nette presenter: 13 | 14 | ```php 15 | addFiles(array( 21 | 'style.css', 22 | WWW_DIR . '/colorbox/colorbox.css', 23 | )); 24 | 25 | $files->addWatchFiles(Finder::findFiles('*.css', '*.less')->in(WWW_DIR . '/css')); 26 | 27 | $compiler = WebLoader\Compiler::createCssCompiler($files, WWW_DIR . '/temp'); 28 | 29 | $compiler->addFilter(new WebLoader\Filter\VariablesFilter(array('foo' => 'bar'))); 30 | $compiler->addFilter(function ($code) { 31 | return cssmin::minify($code, "remove-last-semicolon"); 32 | }); 33 | 34 | $control = new WebLoader\Nette\CssLoader($compiler, '/webtemp'); 35 | $control->setMedia('screen'); 36 | 37 | return $control; 38 | } 39 | ``` 40 | 41 | Template: 42 | 43 | ```html 44 | {control css} 45 | ``` 46 | 47 | Example with Nette Framework extension used 48 | ------------------------------------------- 49 | 50 | Configuration in `app/config/config.neon`: 51 | 52 | ```html 53 | extensions: 54 | webloader: WebLoader\Nette\Extension 55 | 56 | services: 57 | wlCssFilter: WebLoader\Filter\CssUrlsFilter(%wwwDir%) 58 | lessFilter: WebLoader\Filter\LessFilter 59 | jwlCssMinFilter: Joseki\Webloader\CssMinFilter 60 | 61 | webloader: 62 | css: 63 | default: 64 | files: 65 | - style.css 66 | - {files: ["*.css", "*.less"], from: %appDir%/presenters} # Nette\Utils\Finder support 67 | filters: 68 | - @jwlCssMinFilter 69 | fileFilters: 70 | - @lessFilter 71 | - @wlCssFilter 72 | watchFiles: # only watch modify file 73 | - {files: ["*.css", "*.less"], from: css} 74 | - {files: ["*.css", "*.less"], in: css} 75 | 76 | js: 77 | default: 78 | remoteFiles: 79 | - http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js 80 | - http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js 81 | files: 82 | - %appDir%/../libs/nette/nette/client-side/netteForms.js 83 | - web.js 84 | ``` 85 | 86 | For older versions of Nette, you have to register the extension in `app/bootstrap.php`: 87 | 88 | ```php 89 | $webloaderExtension = new \WebLoader\Nette\Extension(); 90 | $webloaderExtension->install($configurator); 91 | ``` 92 | 93 | Usage in `app/presenters/BasePresenter.php`: 94 | 95 | ```php 96 | /** @var \WebLoader\Nette\LoaderFactory @inject */ 97 | public $webLoader; 98 | 99 | /** @return CssLoader */ 100 | protected function createComponentCss() 101 | { 102 | return $this->webLoader->createCssLoader('default'); 103 | } 104 | 105 | /** @return JavaScriptLoader */ 106 | protected function createComponentJs() 107 | { 108 | return $this->webLoader->createJavaScriptLoader('default'); 109 | } 110 | ``` 111 | 112 | 113 | Template: 114 | 115 | ```html 116 | {control css} 117 | {control js} 118 | ``` 119 | -------------------------------------------------------------------------------- /WebLoader/Compiler.php: -------------------------------------------------------------------------------- 1 | collection = $files; 40 | $this->namingConvention = $convention; 41 | $this->setOutputDir($outputDir); 42 | } 43 | 44 | /** 45 | * Create compiler with predefined css output naming convention 46 | * @param IFileCollection $files 47 | * @param string $outputDir 48 | * @return Compiler 49 | */ 50 | public static function createCssCompiler(IFileCollection $files, $outputDir) 51 | { 52 | return new static($files, DefaultOutputNamingConvention::createCssConvention(), $outputDir); 53 | } 54 | 55 | /** 56 | * Create compiler with predefined javascript output naming convention 57 | * @param IFileCollection $files 58 | * @param string $outputDir 59 | * @return Compiler 60 | */ 61 | public static function createJsCompiler(IFileCollection $files, $outputDir) 62 | { 63 | return new static($files, DefaultOutputNamingConvention::createJsConvention(), $outputDir); 64 | } 65 | 66 | /** 67 | * @param bool $allow 68 | */ 69 | public function enableDebugging($allow = TRUE) 70 | { 71 | $this->debugging = (bool) $allow; 72 | } 73 | 74 | /** 75 | * Get temp path 76 | * @return string 77 | */ 78 | public function getOutputDir() 79 | { 80 | return $this->outputDir; 81 | } 82 | 83 | /** 84 | * Set temp path 85 | * @param string $tempPath 86 | */ 87 | public function setOutputDir($tempPath) 88 | { 89 | $tempPath = Path::normalize($tempPath); 90 | 91 | if (!is_dir($tempPath)) { 92 | throw new FileNotFoundException("Temp path '$tempPath' does not exist."); 93 | } 94 | 95 | if (!is_writable($tempPath)) { 96 | throw new InvalidArgumentException("Directory '$tempPath' is not writeable."); 97 | } 98 | 99 | $this->outputDir = $tempPath; 100 | } 101 | 102 | /** 103 | * Get join files 104 | * @return bool 105 | */ 106 | public function getJoinFiles() 107 | { 108 | return $this->joinFiles; 109 | } 110 | 111 | /** 112 | * Set join files 113 | * @param bool $joinFiles 114 | */ 115 | public function setJoinFiles($joinFiles) 116 | { 117 | $this->joinFiles = (bool) $joinFiles; 118 | } 119 | 120 | /** 121 | * Set check last modified 122 | * @param bool $checkLastModified 123 | */ 124 | public function setCheckLastModified($checkLastModified) 125 | { 126 | $this->checkLastModified = (bool) $checkLastModified; 127 | } 128 | 129 | /** 130 | * Get last modified timestamp of newest file 131 | * @param array $files 132 | * @return int 133 | */ 134 | public function getLastModified(array $files = null) 135 | { 136 | if ($files === null) { 137 | $files = $this->collection->getFiles(); 138 | } 139 | 140 | $modified = 0; 141 | 142 | foreach ($files as $file) { 143 | $modified = max($modified, filemtime($file)); 144 | } 145 | 146 | return $modified; 147 | } 148 | 149 | /** 150 | * Get joined content of all files 151 | * @param array $files 152 | * @return string 153 | */ 154 | public function getContent(array $files = null) 155 | { 156 | if ($files === null) { 157 | $files = $this->collection->getFiles(); 158 | } 159 | 160 | // load content 161 | $content = ''; 162 | foreach ($files as $file) { 163 | $content .= PHP_EOL . $this->loadFile($file); 164 | } 165 | 166 | // apply filters 167 | foreach ($this->filters as $filter) { 168 | $content = call_user_func($filter, $content, $this); 169 | } 170 | 171 | return $content; 172 | } 173 | 174 | /** 175 | * Load content and save file 176 | * @param bool $ifModified 177 | * @return array filenames of generated files 178 | */ 179 | public function generate($ifModified = TRUE) 180 | { 181 | $files = $this->collection->getFiles(); 182 | 183 | if (!count($files)) { 184 | return array(); 185 | } 186 | 187 | if ($this->joinFiles) { 188 | $watchFiles = $this->checkLastModified ? array_unique(array_merge($files, $this->collection->getWatchFiles())) : array(); 189 | 190 | return array( 191 | $this->generateFiles($files, $ifModified, $watchFiles), 192 | ); 193 | 194 | } else { 195 | $arr = array(); 196 | 197 | foreach ($files as $file) { 198 | $watchFiles = $this->checkLastModified ? array_unique(array_merge(array($file), $this->collection->getWatchFiles())) : array(); 199 | $arr[] = $this->generateFiles(array($file), $ifModified, $watchFiles); 200 | } 201 | 202 | return $arr; 203 | } 204 | } 205 | 206 | protected function generateFiles(array $files, $ifModified, array $watchFiles = array()) 207 | { 208 | $name = $this->namingConvention->getFilename($files, $this); 209 | $path = $this->outputDir . '/' . $name; 210 | $lastModified = $this->checkLastModified ? $this->getLastModified($watchFiles) : 0; 211 | 212 | if (!$ifModified || !file_exists($path) || $lastModified > filemtime($path) || $this->debugging === TRUE) { 213 | $outPath = in_array('safe', stream_get_wrappers()) ? 'safe://' . $path : $path; 214 | file_put_contents($outPath, $this->getContent($files)); 215 | } 216 | 217 | return (object) array( 218 | 'file' => $name, 219 | 'lastModified' => $lastModified, 220 | 'sourceFiles' => $files, 221 | ); 222 | } 223 | 224 | /** 225 | * Load file 226 | * @param string $file path 227 | * @return string 228 | */ 229 | protected function loadFile($file) 230 | { 231 | $content = file_get_contents($file); 232 | 233 | foreach ($this->fileFilters as $filter) { 234 | $content = call_user_func($filter, $content, $this, $file); 235 | } 236 | 237 | return $content; 238 | } 239 | 240 | /** 241 | * @return \WebLoader\IFileCollection 242 | */ 243 | public function getFileCollection() 244 | { 245 | return $this->collection; 246 | } 247 | 248 | /** 249 | * @return \WebLoader\IOutputNamingConvention 250 | */ 251 | public function getOutputNamingConvention() 252 | { 253 | return $this->namingConvention; 254 | } 255 | 256 | /** 257 | * @param \WebLoader\IFileCollection $collection 258 | */ 259 | public function setFileCollection(IFileCollection $collection) 260 | { 261 | $this->collection = $collection; 262 | } 263 | 264 | /** 265 | * @param \WebLoader\IOutputNamingConvention $namingConvention 266 | */ 267 | public function setOutputNamingConvention(IOutputNamingConvention $namingConvention) 268 | { 269 | $this->namingConvention = $namingConvention; 270 | } 271 | 272 | /** 273 | * @param callback $filter 274 | * @throws InvalidArgumentException 275 | */ 276 | public function addFilter($filter) 277 | { 278 | if (!is_callable($filter)) { 279 | throw new InvalidArgumentException('Filter is not callable.'); 280 | } 281 | 282 | $this->filters[] = $filter; 283 | } 284 | 285 | /** 286 | * @return array 287 | */ 288 | public function getFilters() 289 | { 290 | return $this->filters; 291 | } 292 | 293 | /** 294 | * @param callback $filter 295 | * @throws InvalidArgumentException 296 | */ 297 | public function addFileFilter($filter) 298 | { 299 | if (!is_callable($filter)) { 300 | throw new InvalidArgumentException('Filter is not callable.'); 301 | } 302 | 303 | $this->fileFilters[] = $filter; 304 | } 305 | 306 | /** 307 | * @return array 308 | */ 309 | public function getFileFilters() 310 | { 311 | return $this->fileFilters; 312 | } 313 | 314 | } 315 | -------------------------------------------------------------------------------- /WebLoader/DefaultOutputNamingConvention.php: -------------------------------------------------------------------------------- 1 | setPrefix('cssloader-'); 26 | $convention->setSuffix('.css'); 27 | 28 | return $convention; 29 | } 30 | 31 | /** 32 | * @return DefaultOutputNamingConvention 33 | */ 34 | public static function createJsConvention() 35 | { 36 | $convention = new static(); 37 | $convention->setPrefix('jsloader-'); 38 | $convention->setSuffix('.js'); 39 | 40 | return $convention; 41 | } 42 | 43 | /** 44 | * Get generated file name prefix 45 | * @return string 46 | */ 47 | public function getPrefix() 48 | { 49 | return $this->prefix; 50 | } 51 | 52 | /** 53 | * Set generated file name prefix 54 | * @param string $prefix generated file name prefix 55 | */ 56 | public function setPrefix($prefix) 57 | { 58 | $this->prefix = (string) $prefix; 59 | } 60 | 61 | 62 | /** 63 | * Get generated file name suffix 64 | * @return string 65 | */ 66 | public function getSuffix() 67 | { 68 | return $this->suffix; 69 | } 70 | 71 | 72 | /** 73 | * Set generated file name suffix 74 | * @param string $suffix generated file name suffix 75 | */ 76 | public function setSuffix($suffix) 77 | { 78 | $this->suffix = (string) $suffix; 79 | } 80 | 81 | /** 82 | * Filename of generated file 83 | * @param array $files 84 | * @param \WebLoader\Compiler $compiler 85 | * @return string 86 | */ 87 | public function getFilename(array $files, Compiler $compiler) 88 | { 89 | $name = $this->createHash($files, $compiler); 90 | 91 | if (count($files) === 1) { 92 | $name .= "-" . pathinfo($files[0], PATHINFO_FILENAME); 93 | } 94 | 95 | return $this->prefix . $name . $this->suffix; 96 | } 97 | 98 | protected function createHash(array $files, Compiler $compiler) 99 | { 100 | return substr(md5(implode("|", $files)), 0, 12); 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /WebLoader/FileCollection.php: -------------------------------------------------------------------------------- 1 | root = $root; 31 | } 32 | 33 | /** 34 | * Get file list 35 | * @return array 36 | */ 37 | public function getFiles() 38 | { 39 | return array_values($this->files); 40 | } 41 | 42 | /** 43 | * Make path absolute 44 | * @param $path string 45 | * @throws \WebLoader\FileNotFoundException 46 | * @return string 47 | */ 48 | public function cannonicalizePath($path) 49 | { 50 | $rel = Path::normalize($this->root . "/" . $path); 51 | if (file_exists($rel)) { 52 | return $rel; 53 | } 54 | 55 | $abs = Path::normalize($path); 56 | if (file_exists($abs)) { 57 | return $abs; 58 | } 59 | 60 | throw new FileNotFoundException("File '$path' does not exist."); 61 | } 62 | 63 | 64 | /** 65 | * Add file 66 | * @param $file string filename 67 | */ 68 | public function addFile($file) 69 | { 70 | $file = $this->cannonicalizePath((string) $file); 71 | 72 | if (in_array($file, $this->files, TRUE)) { 73 | return; 74 | } 75 | 76 | $this->files[] = $file; 77 | } 78 | 79 | 80 | /** 81 | * Add files 82 | * @param array|\Traversable $files array list of files 83 | */ 84 | public function addFiles($files) 85 | { 86 | foreach ($files as $file) { 87 | $this->addFile($file); 88 | } 89 | } 90 | 91 | 92 | /** 93 | * Remove file 94 | * @param $file string filename 95 | */ 96 | public function removeFile($file) 97 | { 98 | $this->removeFiles(array($file)); 99 | } 100 | 101 | 102 | /** 103 | * Remove files 104 | * @param array $files list of files 105 | */ 106 | public function removeFiles(array $files) 107 | { 108 | $files = array_map(array($this, 'cannonicalizePath'), $files); 109 | $this->files = array_diff($this->files, $files); 110 | } 111 | 112 | 113 | /** 114 | * Add file in remote repository (for example Google CDN). 115 | * @param string $file URL address 116 | */ 117 | public function addRemoteFile($file) 118 | { 119 | if (in_array($file, $this->remoteFiles)) { 120 | return; 121 | } 122 | 123 | $this->remoteFiles[] = $file; 124 | } 125 | 126 | /** 127 | * Add multiple remote files 128 | * @param array|\Traversable $files 129 | */ 130 | public function addRemoteFiles($files) 131 | { 132 | foreach ($files as $file) { 133 | $this->addRemoteFile($file); 134 | } 135 | } 136 | 137 | /** 138 | * Remove all files 139 | */ 140 | public function clear() 141 | { 142 | $this->files = array(); 143 | $this->watchFiles = array(); 144 | $this->remoteFiles = array(); 145 | } 146 | 147 | /** 148 | * @return array 149 | */ 150 | public function getRemoteFiles() 151 | { 152 | return $this->remoteFiles; 153 | } 154 | 155 | /** 156 | * @return string 157 | */ 158 | public function getRoot() 159 | { 160 | return $this->root; 161 | } 162 | 163 | /** 164 | * Add watch file 165 | * @param $file string filename 166 | */ 167 | public function addWatchFile($file) 168 | { 169 | $file = $this->cannonicalizePath((string) $file); 170 | 171 | if (in_array($file, $this->watchFiles, TRUE)) { 172 | return; 173 | } 174 | 175 | $this->watchFiles[] = $file; 176 | } 177 | 178 | /** 179 | * Add watch files 180 | * @param array|\Traversable $files array list of files 181 | */ 182 | public function addWatchFiles($files) 183 | { 184 | foreach ($files as $file) { 185 | $this->addWatchFile($file); 186 | } 187 | } 188 | 189 | /** 190 | * Get watch file list 191 | * @return array 192 | */ 193 | public function getWatchFiles() 194 | { 195 | return array_values($this->watchFiles); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /WebLoader/FileNotFoundException.php: -------------------------------------------------------------------------------- 1 | bin = $bin; 26 | } 27 | 28 | /** 29 | * Invoke filter 30 | * 31 | * @param string 32 | * @param \WebLoader\Compiler 33 | * @param string 34 | * @return string 35 | */ 36 | public function __invoke($code, \WebLoader\Compiler $loader, $file = NULL) 37 | { 38 | if (pathinfo($file, PATHINFO_EXTENSION) === 'coffee') { 39 | $code = $this->compileCoffee($code); 40 | } 41 | 42 | return $code; 43 | } 44 | 45 | /** 46 | * @param string 47 | * @param bool|NULL 48 | * @return string 49 | */ 50 | public function compileCoffee($source, $bare = NULL) 51 | { 52 | if (is_null($bare)) { 53 | $bare = $this->bare; 54 | } 55 | 56 | $cmd = $this->bin . ' -p -s' . ($bare ? ' -b' : ''); 57 | 58 | return Process::run($cmd, $source); 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /WebLoader/Filter/CssUrlsFilter.php: -------------------------------------------------------------------------------- 1 | docRoot = \WebLoader\Path::normalize($docRoot); 32 | 33 | if (!is_dir($this->docRoot)) { 34 | throw new \WebLoader\InvalidArgumentException('Given document root is not directory.'); 35 | } 36 | 37 | $this->basePath = $basePath; 38 | } 39 | 40 | /** 41 | * @param string $basePath 42 | */ 43 | public function setBasePath($basePath) 44 | { 45 | $this->basePath = $basePath; 46 | } 47 | 48 | /** 49 | * Make relative url absolute 50 | * @param string $url image url 51 | * @param string $quote single or double quote 52 | * @param string $cssFile absolute css file path 53 | * @return string 54 | */ 55 | public function absolutizeUrl($url, $quote, $cssFile) 56 | { 57 | // is already absolute 58 | if (preg_match('/^([a-z]+:\/)?\//', $url)) { 59 | return $url; 60 | } 61 | 62 | $cssFile = \WebLoader\Path::normalize($cssFile); 63 | 64 | // inside document root 65 | if (strncmp($cssFile, $this->docRoot, strlen($this->docRoot)) === 0) { 66 | $path = $this->basePath . substr(dirname($cssFile), strlen($this->docRoot)) . DIRECTORY_SEPARATOR . $url; 67 | } else { 68 | // outside document root we don't know 69 | return $url; 70 | } 71 | 72 | $path = $this->cannonicalizePath($path); 73 | 74 | return $quote === '"' ? addslashes($path) : $path; 75 | } 76 | 77 | /** 78 | * Cannonicalize path 79 | * @param string $path 80 | * @return string path 81 | */ 82 | public function cannonicalizePath($path) 83 | { 84 | $path = strtr($path, DIRECTORY_SEPARATOR, '/'); 85 | 86 | $pathArr = array(); 87 | foreach (explode('/', $path) as $i => $name) { 88 | if ($name === '.' || ($name === '' && $i > 0)) continue; 89 | 90 | if ($name === '..') { 91 | array_pop($pathArr); 92 | continue; 93 | } 94 | 95 | $pathArr[] = $name; 96 | } 97 | 98 | return implode('/', $pathArr); 99 | } 100 | 101 | /** 102 | * Invoke filter 103 | * @param string $code 104 | * @param \WebLoader\Compiler $loader 105 | * @param string $file 106 | * @return string 107 | */ 108 | public function __invoke($code, \WebLoader\Compiler $loader, $file = null) 109 | { 110 | // thanks to kravco 111 | $regexp = '~ 112 | (?absolutizeUrl($matches[2], $matches[1], $file) . "')"; 134 | }, $code); 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /WebLoader/Filter/LessBinFilter.php: -------------------------------------------------------------------------------- 1 | bin = $bin; 27 | $this->env = $env + $_ENV; 28 | unset($this->env['argv'], $this->env['argc']); 29 | } 30 | 31 | /** 32 | * Invoke filter 33 | * @param string $code 34 | * @param \WebLoader\Compiler $loader 35 | * @param string $file 36 | * @return string 37 | */ 38 | public function __invoke($code, \WebLoader\Compiler $loader, $file) 39 | { 40 | if (pathinfo($file, PATHINFO_EXTENSION) === 'less') { 41 | $code = Process::run("{$this->bin} -", $code, dirname($file), $this->env); 42 | } 43 | 44 | return $code; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /WebLoader/Filter/LessFilter.php: -------------------------------------------------------------------------------- 1 | lc = $lc; 19 | } 20 | 21 | /** 22 | * @return \lessc 23 | */ 24 | private function getLessC() 25 | { 26 | // lazy loading 27 | if (empty($this->lc)) { 28 | $this->lc = new \lessc(); 29 | } 30 | 31 | return clone $this->lc; 32 | } 33 | 34 | /** 35 | * Invoke filter 36 | * @param string $code 37 | * @param \WebLoader\Compiler $loader 38 | * @param string $file 39 | * @return string 40 | */ 41 | public function __invoke($code, \WebLoader\Compiler $loader, $file) 42 | { 43 | if (pathinfo($file, PATHINFO_EXTENSION) === 'less') { 44 | $lessc = $this->getLessC(); 45 | $lessc->importDir = pathinfo($file, PATHINFO_DIRNAME) . '/'; 46 | return $lessc->compile($code); 47 | } 48 | 49 | return $code; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /WebLoader/Filter/PHPCoffeeScriptFilter.php: -------------------------------------------------------------------------------- 1 | compileCoffee($code, $file); 29 | } 30 | 31 | return $code; 32 | } 33 | 34 | 35 | /** 36 | * @param $source $string 37 | * @param $file bool|NULL 38 | * @throws \WebLoader\WebLoaderException 39 | * @return string 40 | */ 41 | public function compileCoffee($source, $file) 42 | { 43 | try { 44 | return Compiler::compile($source, array('filename' => $file)); 45 | } catch (\Exception $e) { 46 | throw new \WebLoader\WebLoaderException('CoffeeScript Filter Error: ' . $e->getMessage(), 0, $e); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /WebLoader/Filter/Process.php: -------------------------------------------------------------------------------- 1 | array('pipe', 'r'), // stdin 26 | 1 => array('pipe', 'w'), // stdout 27 | 2 => array('pipe', 'w'), // stderr 28 | ); 29 | 30 | $pipes = array(); 31 | $proc = proc_open($cmd, $descriptorspec, $pipes, $cwd, $env); 32 | 33 | if (!empty($stdin)) { 34 | fwrite($pipes[0], $stdin . PHP_EOL); 35 | } 36 | fclose($pipes[0]); 37 | 38 | $stdout = stream_get_contents($pipes[1]); 39 | $stderr = stream_get_contents($pipes[2]); 40 | 41 | $code = proc_close($proc); 42 | 43 | if ($code != 0) { 44 | throw new \RuntimeException($stderr, $code); 45 | } 46 | 47 | return $stdout; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /WebLoader/Filter/ScssFilter.php: -------------------------------------------------------------------------------- 1 | sc = $sc; 22 | } 23 | 24 | /** 25 | * @return \Leafo\ScssPhp\Compiler|\scssc 26 | */ 27 | private function getScssC() 28 | { 29 | // lazy loading 30 | if (empty($this->sc)) { 31 | $this->sc = new \Leafo\ScssPhp\Compiler(); 32 | } 33 | 34 | return $this->sc; 35 | } 36 | 37 | /** 38 | * Invoke filter 39 | * @param string $code 40 | * @param \WebLoader\Compiler $loader 41 | * @param string $file 42 | * @return string 43 | */ 44 | public function __invoke($code, \WebLoader\Compiler $loader, $file) 45 | { 46 | if (pathinfo($file, PATHINFO_EXTENSION) === 'scss') { 47 | $this->getScssC()->setImportPaths(array('', pathinfo($file, PATHINFO_DIRNAME) . '/')); 48 | return $this->getScssC()->compile($code); 49 | } 50 | 51 | return $code; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /WebLoader/Filter/StylusFilter.php: -------------------------------------------------------------------------------- 1 | bin = $bin; 29 | } 30 | 31 | /** 32 | * Invoke filter 33 | * 34 | * @param string 35 | * @param \WebLoader\Compiler 36 | * @param string 37 | * @return string 38 | */ 39 | public function __invoke($code, \WebLoader\Compiler $loader, $file = NULL) 40 | { 41 | if (pathinfo($file, PATHINFO_EXTENSION) === 'styl') { 42 | $path = 43 | $cmd = $this->bin . ($this->compress ? ' -c' : '') . ($this->includeCss ? ' --include-css' : '') . ' -I ' . pathinfo($file, PATHINFO_DIRNAME); 44 | try { 45 | $code = Process::run($cmd, $code); 46 | } catch (\RuntimeException $e) { 47 | throw new \WebLoader\WebLoaderException('Stylus Filter Error', 0, $e); 48 | } 49 | } 50 | 51 | return $code; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /WebLoader/Filter/TypeScriptFilter.php: -------------------------------------------------------------------------------- 1 | bin = $bin; 27 | $this->env = $env + $_ENV; 28 | unset($this->env['argv'], $this->env['argc']); 29 | } 30 | 31 | /** 32 | * Invoke filter 33 | * 34 | * @param string $code 35 | * @param \WebLoader\Compiler $compiler 36 | * @param string $file 37 | * @return string 38 | */ 39 | public function __invoke($code, \WebLoader\Compiler $compiler, $file = NULL) 40 | { 41 | if (pathinfo($file, PATHINFO_EXTENSION) === 'ts') { 42 | $out = substr_replace($file, 'js', -2); 43 | $cmd = sprintf("%s %s --target ES5 --out %s", $this->bin, escapeshellarg($file), escapeshellarg($out)); 44 | Process::run($cmd, NULL, NULL, $this->env); 45 | $code = file_get_contents($out); 46 | } 47 | 48 | return $code; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /WebLoader/Filter/VariablesFilter.php: -------------------------------------------------------------------------------- 1 | $value) { 30 | $this->$key = $value; 31 | } 32 | } 33 | 34 | /** 35 | * Set delimiter 36 | * @param string $start 37 | * @param string $end 38 | * @return VariablesFilter 39 | */ 40 | public function setDelimiter($start, $end) 41 | { 42 | $this->startVariable = (string)$start; 43 | $this->endVariable = (string)$end; 44 | return $this; 45 | } 46 | 47 | /** 48 | * Invoke filter 49 | * @param string $code 50 | * @return string 51 | */ 52 | public function __invoke($code) 53 | { 54 | $start = $this->startVariable; 55 | $end = $this->endVariable; 56 | 57 | $variables = array_map(function ($key) use ($start, $end) { 58 | return $start . $key . $end; 59 | }, array_keys($this->variables)); 60 | 61 | $values = array_values($this->variables); 62 | 63 | return str_replace($variables, $values, $code); 64 | } 65 | 66 | /** 67 | * Magic set variable, do not call directly 68 | * @param string $name 69 | * @param string $value 70 | */ 71 | public function __set($name, $value) 72 | { 73 | $this->variables[$name] = (string) $value; 74 | } 75 | 76 | /** 77 | * Magic get variable, do not call directly 78 | * @param string $name 79 | * @return string 80 | * @throws \WebLoader\InvalidArgumentException 81 | */ 82 | public function & __get($name) 83 | { 84 | if (array_key_exists($name, $this->variables)) { 85 | return $this->variables[$name]; 86 | } else { 87 | throw new \WebLoader\InvalidArgumentException("Variable '$name' is not set."); 88 | } 89 | } 90 | 91 | } -------------------------------------------------------------------------------- /WebLoader/IFileCollection.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | class CompilationException extends \WebLoader\WebLoaderException 9 | { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /WebLoader/Nette/CssLoader.php: -------------------------------------------------------------------------------- 1 | media; 35 | } 36 | 37 | /** 38 | * Get type 39 | * @return string 40 | */ 41 | public function getType() 42 | { 43 | return $this->type; 44 | } 45 | 46 | /** 47 | * Get title 48 | * @return string 49 | */ 50 | public function getTitle() 51 | { 52 | return $this->title; 53 | } 54 | 55 | /** 56 | * Is alternate ? 57 | * @return bool 58 | */ 59 | public function isAlternate() 60 | { 61 | return $this->alternate; 62 | } 63 | 64 | /** 65 | * Set media 66 | * @param string $media 67 | * @return CssLoader 68 | */ 69 | public function setMedia($media) 70 | { 71 | $this->media = $media; 72 | return $this; 73 | } 74 | 75 | /** 76 | * Set type 77 | * @param string $type 78 | * @return CssLoader 79 | */ 80 | public function setType($type) 81 | { 82 | $this->type = $type; 83 | return $this; 84 | } 85 | 86 | /** 87 | * Set title 88 | * @param string $title 89 | * @return CssLoader 90 | */ 91 | public function setTitle($title) 92 | { 93 | $this->title = $title; 94 | return $this; 95 | } 96 | 97 | /** 98 | * Set alternate 99 | * @param bool $alternate 100 | * @return CssLoader 101 | */ 102 | public function setAlternate($alternate) 103 | { 104 | $this->alternate = $alternate; 105 | return $this; 106 | } 107 | 108 | /** 109 | * Get link element 110 | * @param string $source 111 | * @return Html 112 | */ 113 | public function getElement($source) 114 | { 115 | if ($this->alternate) { 116 | $alternate = ' alternate'; 117 | } else { 118 | $alternate = ''; 119 | } 120 | 121 | return Html::el("link")->rel("stylesheet".$alternate)->type($this->type)->media($this->media)->title($this->title)->href($source); 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /WebLoader/Nette/CssUrlFilter.php: -------------------------------------------------------------------------------- 1 | getUrl()->getBasePath()); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /WebLoader/Nette/Diagnostics/Panel.php: -------------------------------------------------------------------------------- 1 | 'CSS files', 23 | 'js' => 'JavaScript files', 24 | 'less' => 'Less files', 25 | 'scss' => 'Sass files', 26 | 'coffee' => 'CoffeeScript files' 27 | ); 28 | 29 | /** @var Compiler[] */ 30 | private $compilers = array(); 31 | 32 | /** @var array */ 33 | private $size; 34 | 35 | /** @var array */ 36 | private $files; 37 | 38 | /** @var array */ 39 | private $sizes; 40 | 41 | /** @var string */ 42 | private $root; 43 | 44 | /** 45 | * @param string 46 | */ 47 | public function __construct($appDir = NULL) 48 | { 49 | $this->root = $appDir ? str_replace('\\', DIRECTORY_SEPARATOR, realpath(dirname($appDir))) : ''; 50 | Debugger::getBar()->addPanel($this); 51 | } 52 | 53 | /** 54 | * Registers a compiler. 55 | * 56 | * @param string $name 57 | * @param Compiler $compiler 58 | * @return Panel 59 | */ 60 | public function addLoader($name, Compiler $compiler) 61 | { 62 | $this->compilers[$name] = $compiler; 63 | return $this; 64 | } 65 | 66 | /** 67 | * Computes the info. 68 | * @return array 69 | */ 70 | private function compute() 71 | { 72 | if ($this->size !== NULL) { 73 | return $this->size; 74 | } 75 | 76 | $size = array( 77 | 'original' => 0, 78 | 'combined' => 0 79 | ); 80 | $this->files = $this->sizes = array(); 81 | 82 | foreach ($this->compilers as $name => $compiler) { 83 | $group = lcfirst(substr($name, $name[0] === 'c' ? 3 : 2)); 84 | 85 | if (!isset($this->files[$group])) { 86 | $this->files[$group] = array(); 87 | } 88 | if (!isset($this->sizes[$group])) { 89 | $this->sizes[$group] = array('.' => array('original' => 0, 'combined' => 0)); 90 | } 91 | 92 | $compilerCombinedSize = 0; 93 | foreach ($compiler->generate() as $generated) { 94 | $generatedSize = filesize($compiler->getOutputDir() . DIRECTORY_SEPARATOR . $generated->file); 95 | $size['combined'] += $generatedSize; 96 | $compilerCombinedSize += $generatedSize; 97 | 98 | foreach ($generated->sourceFiles as $file) { 99 | $extension = strtolower(pathinfo($file, PATHINFO_EXTENSION)); 100 | $file = str_replace('\\', DIRECTORY_SEPARATOR, realpath($file)); 101 | 102 | if (!isset($this->files[$group][$extension])) { 103 | $this->files[$group][$extension] = array(); 104 | } 105 | if (!isset($this->sizes[$group][$extension])) { 106 | $this->sizes[$group][$extension] = array('original' => 0); 107 | } 108 | 109 | $this->files[$group][$extension][] = array( 110 | 'name' => substr($file, strlen($this->root) + 1), 111 | 'full' => $file, 112 | 'size' => $fileSize = filesize($file) 113 | ); 114 | 115 | $size['original'] += $fileSize; 116 | $this->sizes[$group][$extension]['original'] += $fileSize; 117 | $this->sizes[$group]['.']['original'] += $fileSize; 118 | } 119 | } 120 | 121 | $this->sizes[$group]['.']['combined'] += $compilerCombinedSize; 122 | } 123 | 124 | return $this->size = $size + array('ratio' => $size['original'] !== 0 ? ($size['combined'] / $size['original']) * 100 : 0); 125 | } 126 | 127 | /** 128 | * Renders loaded files table. 129 | * @return string 130 | */ 131 | private function getTable() 132 | { 133 | $latte = new Latte\Engine; 134 | 135 | $latte->addFilter('extension', function($extension) { 136 | return isset(Panel::$types[$extension]) ? Panel::$types[$extension] : $extension; 137 | }); 138 | 139 | return $latte->renderToString(__DIR__ . '/panel.latte', array( 140 | 'files' => $this->files, 141 | 'sizes' => $this->sizes, 142 | 'size' => $this->size 143 | )); 144 | } 145 | 146 | /** 147 | * Returns panel content. 148 | * @return string 149 | */ 150 | public function getPanel() 151 | { 152 | return $this->compute() ? $this->getTable() : ''; 153 | } 154 | 155 | /** 156 | * Returns panel tab. 157 | * @return string 158 | */ 159 | public function getTab() 160 | { 161 | $this->compute(); 162 | 163 | return '' 164 | . '' 165 | . Filters::bytes($this->size['combined']) 166 | . ''; 167 | } 168 | 169 | } 170 | -------------------------------------------------------------------------------- /WebLoader/Nette/Diagnostics/panel.latte: -------------------------------------------------------------------------------- 1 |

WebLoader

2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 28 | 29 | 30 |
{$group}{$sizes[$group]['.']['original']|bytes} original, {$sizes[$group]['.']['combined']|bytes} combined
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
{$extension|extension}{$sizes[$group][$extension]['original']|bytes}
{$file['name']}{$file['size']|bytes}
27 |
31 |

32 | Loaded resources original size: {$size['original']|bytes}
33 | Combined resources size: {$size['combined']|bytes}
34 | Ratio: {$size['ratio']|number:0}% 35 |

36 |
37 | -------------------------------------------------------------------------------- /WebLoader/Nette/Extension.php: -------------------------------------------------------------------------------- 1 | array( 29 | 'checkLastModified' => TRUE, 30 | 'debug' => FALSE, 31 | 'sourceDir' => '%wwwDir%/js', 32 | 'tempDir' => '%wwwDir%/' . self::DEFAULT_TEMP_PATH, 33 | 'tempPath' => self::DEFAULT_TEMP_PATH, 34 | 'files' => array(), 35 | 'watchFiles' => array(), 36 | 'remoteFiles' => array(), 37 | 'filters' => array(), 38 | 'fileFilters' => array(), 39 | 'joinFiles' => TRUE, 40 | 'namingConvention' => '@' . $this->prefix('jsNamingConvention'), 41 | ), 42 | 'cssDefaults' => array( 43 | 'checkLastModified' => TRUE, 44 | 'debug' => FALSE, 45 | 'sourceDir' => '%wwwDir%/css', 46 | 'tempDir' => '%wwwDir%/' . self::DEFAULT_TEMP_PATH, 47 | 'tempPath' => self::DEFAULT_TEMP_PATH, 48 | 'files' => array(), 49 | 'watchFiles' => array(), 50 | 'remoteFiles' => array(), 51 | 'filters' => array(), 52 | 'fileFilters' => array(), 53 | 'joinFiles' => TRUE, 54 | 'namingConvention' => '@' . $this->prefix('cssNamingConvention'), 55 | ), 56 | 'js' => array( 57 | 58 | ), 59 | 'css' => array( 60 | 61 | ), 62 | 'debugger' => '%debugMode%' 63 | ); 64 | } 65 | 66 | public function loadConfiguration() 67 | { 68 | $builder = $this->getContainerBuilder(); 69 | $config = $this->getConfig($this->getDefaultConfig()); 70 | 71 | $builder->addDefinition($this->prefix('cssNamingConvention')) 72 | ->setFactory('WebLoader\DefaultOutputNamingConvention::createCssConvention'); 73 | 74 | $builder->addDefinition($this->prefix('jsNamingConvention')) 75 | ->setFactory('WebLoader\DefaultOutputNamingConvention::createJsConvention'); 76 | 77 | if ($config['debugger']) { 78 | $builder->addDefinition($this->prefix('tracyPanel')) 79 | ->setClass('WebLoader\Nette\Diagnostics\Panel') 80 | ->setArguments(array($builder->expand('%appDir%'))); 81 | } 82 | 83 | $builder->parameters['webloader'] = $config; 84 | 85 | $loaderFactoryTempPaths = array(); 86 | 87 | foreach (array('css', 'js') as $type) { 88 | foreach ($config[$type] as $name => $wlConfig) { 89 | $wlConfig = Helpers::merge($wlConfig, $config[$type . 'Defaults']); 90 | $this->addWebLoader($builder, $type . ucfirst($name), $wlConfig); 91 | $loaderFactoryTempPaths[strtolower($name)] = $wlConfig['tempPath']; 92 | 93 | if (!is_dir($wlConfig['tempDir']) || !is_writable($wlConfig['tempDir'])) { 94 | throw new CompilationException(sprintf("You must create a writable directory '%s'", $wlConfig['tempDir'])); 95 | } 96 | } 97 | } 98 | 99 | $builder->addDefinition($this->prefix('factory')) 100 | ->setClass('WebLoader\Nette\LoaderFactory', array($loaderFactoryTempPaths, $this->name)); 101 | } 102 | 103 | private function addWebLoader(ContainerBuilder $builder, $name, $config) 104 | { 105 | $filesServiceName = $this->prefix($name . 'Files'); 106 | 107 | $files = $builder->addDefinition($filesServiceName) 108 | ->setClass('WebLoader\FileCollection') 109 | ->setArguments(array($config['sourceDir'])); 110 | 111 | foreach ($this->findFiles($config['files'], $config['sourceDir']) as $file) { 112 | $files->addSetup('addFile', array($file)); 113 | } 114 | 115 | foreach ($this->findFiles($config['watchFiles'], $config['sourceDir']) as $file) { 116 | $files->addSetup('addWatchFile', array($file)); 117 | } 118 | 119 | $files->addSetup('addRemoteFiles', array($config['remoteFiles'])); 120 | 121 | $compiler = $builder->addDefinition($this->prefix($name . 'Compiler')) 122 | ->setClass('WebLoader\Compiler') 123 | ->setArguments(array( 124 | '@' . $filesServiceName, 125 | $config['namingConvention'], 126 | $config['tempDir'], 127 | )); 128 | 129 | $compiler->addSetup('setJoinFiles', array($config['joinFiles'])); 130 | 131 | if ($builder->parameters['webloader']['debugger']) { 132 | $compiler->addSetup('@' . $this->prefix('tracyPanel') . '::addLoader', array( 133 | $name, 134 | '@' . $this->prefix($name . 'Compiler') 135 | )); 136 | } 137 | 138 | foreach ($config['filters'] as $filter) { 139 | $compiler->addSetup('addFilter', array($filter)); 140 | } 141 | 142 | foreach ($config['fileFilters'] as $filter) { 143 | $compiler->addSetup('addFileFilter', array($filter)); 144 | } 145 | 146 | if (isset($config['debug']) && $config['debug']) { 147 | $compiler->addSetup('enableDebugging'); 148 | } 149 | 150 | $compiler->addSetup('setCheckLastModified', array($config['checkLastModified'])); 151 | 152 | // todo css media 153 | } 154 | 155 | public function afterCompile(Nette\PhpGenerator\ClassType $class) 156 | { 157 | $meta = $class->properties['meta']; 158 | if (array_key_exists('webloader\\nette\\loaderfactory', $meta->value['types'])) { 159 | $meta->value['types']['webloader\\loaderfactory'] = $meta->value['types']['webloader\\nette\\loaderfactory']; 160 | } 161 | if (array_key_exists('WebLoader\\Nette\\LoaderFactory', $meta->value['types'])) { 162 | $meta->value['types']['WebLoader\\LoaderFactory'] = $meta->value['types']['WebLoader\\Nette\\LoaderFactory']; 163 | } 164 | 165 | $init = $class->methods['initialize']; 166 | $init->addBody('if (!class_exists(?, ?)) class_alias(?, ?);', array('WebLoader\\LoaderFactory', FALSE, 'WebLoader\\Nette\\LoaderFactory', 'WebLoader\\LoaderFactory')); 167 | } 168 | 169 | public function install(Configurator $configurator) 170 | { 171 | $self = $this; 172 | $configurator->onCompile[] = function ($configurator, Compiler $compiler) use ($self) { 173 | $compiler->addExtension($self::EXTENSION_NAME, $self); 174 | }; 175 | } 176 | 177 | /** 178 | * @param array $filesConfig 179 | * @param string $sourceDir 180 | * @return array 181 | */ 182 | private function findFiles(array $filesConfig, $sourceDir) 183 | { 184 | $normalizedFiles = array(); 185 | 186 | foreach ($filesConfig as $file) { 187 | // finder support 188 | if (is_array($file) && isset($file['files']) && (isset($file['in']) || isset($file['from']))) { 189 | $finder = Finder::findFiles($file['files']); 190 | 191 | if (isset($file['exclude'])) { 192 | $finder->exclude($file['exclude']); 193 | } 194 | 195 | if (isset($file['in'])) { 196 | $finder->in(is_dir($file['in']) ? $file['in'] : $sourceDir . DIRECTORY_SEPARATOR . $file['in']); 197 | } else { 198 | $finder->from(is_dir($file['from']) ? $file['from'] : $sourceDir . DIRECTORY_SEPARATOR . $file['from']); 199 | } 200 | 201 | $foundFilesList = array(); 202 | foreach ($finder as $foundFile) { 203 | /** @var \SplFileInfo $foundFile */ 204 | $foundFilesList[] = $foundFile->getPathname(); 205 | } 206 | 207 | natsort($foundFilesList); 208 | 209 | foreach ($foundFilesList as $foundFilePathname) { 210 | $normalizedFiles[] = $foundFilePathname; 211 | } 212 | 213 | } else { 214 | $this->checkFileExists($file, $sourceDir); 215 | $normalizedFiles[] = $file; 216 | } 217 | } 218 | 219 | return $normalizedFiles; 220 | } 221 | 222 | /** 223 | * @param string $file 224 | * @param string $sourceDir 225 | * @throws FileNotFoundException 226 | */ 227 | protected function checkFileExists($file, $sourceDir) 228 | { 229 | if (!file_exists($file)) { 230 | $tmp = rtrim($sourceDir, '/\\') . DIRECTORY_SEPARATOR . $file; 231 | if (!file_exists($tmp)) { 232 | throw new FileNotFoundException(sprintf("Neither '%s' or '%s' was found", $file, $tmp)); 233 | } 234 | } 235 | } 236 | 237 | } 238 | -------------------------------------------------------------------------------- /WebLoader/Nette/JavaScriptLoader.php: -------------------------------------------------------------------------------- 1 | src($source); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /WebLoader/Nette/LoaderFactory.php: -------------------------------------------------------------------------------- 1 | httpRequest = $httpRequest; 33 | $this->serviceLocator = $serviceLocator; 34 | $this->tempPaths = $tempPaths; 35 | $this->extensionName = $extensionName; 36 | } 37 | 38 | /** 39 | * @param string $name 40 | * @return \WebLoader\Nette\CssLoader 41 | */ 42 | public function createCssLoader($name) 43 | { 44 | /** @var Compiler $compiler */ 45 | $compiler = $this->serviceLocator->getService($this->extensionName . '.css' . ucfirst($name) . 'Compiler'); 46 | return new CssLoader($compiler, $this->formatTempPath($name)); 47 | } 48 | 49 | /** 50 | * @param string $name 51 | * @return \WebLoader\Nette\JavaScriptLoader 52 | */ 53 | public function createJavaScriptLoader($name) 54 | { 55 | /** @var Compiler $compiler */ 56 | $compiler = $this->serviceLocator->getService($this->extensionName . '.js' . ucfirst($name) . 'Compiler'); 57 | return new JavaScriptLoader($compiler, $this->formatTempPath($name)); 58 | } 59 | 60 | /** 61 | * @param string $name 62 | * @return string 63 | */ 64 | private function formatTempPath($name) 65 | { 66 | $lName = strtolower($name); 67 | $tempPath = isset($this->tempPaths[$lName]) ? $this->tempPaths[$lName] : Extension::DEFAULT_TEMP_PATH; 68 | return rtrim($this->httpRequest->getUrl()->basePath, '/') . '/' . $tempPath; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /WebLoader/Nette/WebLoader.php: -------------------------------------------------------------------------------- 1 | compiler = $compiler; 26 | $this->tempPath = $tempPath; 27 | } 28 | 29 | /** 30 | * @return \WebLoader\Compiler 31 | */ 32 | public function getCompiler() 33 | { 34 | return $this->compiler; 35 | } 36 | 37 | /** 38 | * @param \WebLoader\Compiler 39 | */ 40 | public function setCompiler(Compiler $compiler) 41 | { 42 | $this->compiler = $compiler; 43 | } 44 | 45 | /** 46 | * @return string 47 | */ 48 | public function getTempPath() 49 | { 50 | return $this->tempPath; 51 | } 52 | 53 | /** 54 | * @param string 55 | */ 56 | public function setTempPath($tempPath) 57 | { 58 | $this->tempPath = $tempPath; 59 | } 60 | 61 | /** 62 | * Get html element including generated content 63 | * @param string $source 64 | * @return \Nette\Utils\Html 65 | */ 66 | abstract public function getElement($source); 67 | 68 | /** 69 | * Generate compiled file(s) and render link(s) 70 | */ 71 | public function render() 72 | { 73 | $hasArgs = func_num_args() > 0; 74 | 75 | if ($hasArgs) { 76 | $backup = $this->compiler->getFileCollection(); 77 | $newFiles = new FileCollection($backup->getRoot()); 78 | $newFiles->addFiles(func_get_args()); 79 | $this->compiler->setFileCollection($newFiles); 80 | } 81 | 82 | // remote files 83 | foreach ($this->compiler->getFileCollection()->getRemoteFiles() as $file) { 84 | echo $this->getElement($file), PHP_EOL; 85 | } 86 | 87 | foreach ($this->compiler->generate() as $file) { 88 | echo $this->getElement($this->getGeneratedFilePath($file)), PHP_EOL; 89 | } 90 | 91 | if ($hasArgs) { 92 | $this->compiler->setFileCollection($backup); 93 | } 94 | } 95 | 96 | protected function getGeneratedFilePath($file) 97 | { 98 | return $this->tempPath . '/' . $file->file . '?' . $file->lastModified; 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /WebLoader/Path.php: -------------------------------------------------------------------------------- 1 | shouldReceive('getFiles')->andReturn(array( 23 | __DIR__ . '/fixtures/a.txt', 24 | __DIR__ . '/fixtures/b.txt', 25 | __DIR__ . '/fixtures/c.txt', 26 | )); 27 | $fileCollection->shouldReceive('getWatchFiles')->andReturn(array( 28 | __DIR__ . '/fixtures/a.txt', 29 | __DIR__ . '/fixtures/b.txt', 30 | __DIR__ . '/fixtures/c.txt', 31 | )); 32 | 33 | $convention = Mockery::mock('WebLoader\IOutputNamingConvention'); 34 | $convention->shouldReceive('getFilename')->andReturnUsing(function ($files, $compiler) { 35 | return 'webloader-' . md5(join(',', $files)); 36 | }); 37 | 38 | $this->object = new Compiler($fileCollection, $convention, __DIR__ . '/temp'); 39 | 40 | foreach ($this->getTempFiles() as $file) { 41 | unlink($file); 42 | } 43 | } 44 | 45 | /** 46 | * @return array 47 | */ 48 | private function getTempFiles() 49 | { 50 | return glob(__DIR__ . '/temp/webloader-*'); 51 | } 52 | 53 | public function testJoinFiles() 54 | { 55 | $this->assertTrue($this->object->getJoinFiles()); 56 | 57 | $ret = $this->object->generate(); 58 | $this->assertEquals(1, count($ret), 'Multiple files are generated instead of join.'); 59 | $this->assertEquals(1, count($this->getTempFiles()), 'Multiple files are generated instead of join.'); 60 | } 61 | 62 | public function testEmptyFiles() 63 | { 64 | $this->assertTrue($this->object->getJoinFiles()); 65 | $this->object->setFileCollection(new \WebLoader\FileCollection()); 66 | 67 | $ret = $this->object->generate(); 68 | $this->assertEquals(0, count($ret)); 69 | $this->assertEquals(0, count($this->getTempFiles())); 70 | } 71 | 72 | public function testNotJoinFiles() 73 | { 74 | $this->object->setJoinFiles(FALSE); 75 | $this->assertFalse($this->object->getJoinFiles()); 76 | 77 | $ret = $this->object->generate(); 78 | $this->assertEquals(3, count($ret), 'Wrong file count generated.'); 79 | $this->assertEquals(3, count($this->getTempFiles()), 'Wrong file count generated.'); 80 | } 81 | 82 | /** 83 | * @expectedException \WebLoader\FileNotFoundException 84 | */ 85 | public function testSetOutDir() 86 | { 87 | $this->object->setOutputDir('blablabla'); 88 | } 89 | 90 | public function testGeneratingAndFilters() 91 | { 92 | $this->object->addFileFilter(function ($code) { 93 | return strrev($code); 94 | }); 95 | $this->object->addFileFilter(function ($code, Compiler $compiler, $file) { 96 | return pathinfo($file, PATHINFO_FILENAME) . ':' . $code . ','; 97 | }); 98 | $this->object->addFilter(function ($code, Compiler $compiler) { 99 | return '-' . $code; 100 | }); 101 | $this->object->addFilter(function ($code) { 102 | return $code . $code; 103 | }); 104 | 105 | $expectedContent = '-' . PHP_EOL . 'a:cba,' . PHP_EOL . 'b:fed,' . PHP_EOL . 106 | 'c:ihg,-' . PHP_EOL . 'a:cba,' . PHP_EOL . 'b:fed,' . PHP_EOL . 'c:ihg,'; 107 | 108 | $files = $this->object->generate(); 109 | 110 | $this->assertTrue(is_numeric($files[0]->lastModified), 'Generate does not provide last modified timestamp correctly.'); 111 | 112 | $content = file_get_contents($this->object->getOutputDir() . '/' . $files[0]->file); 113 | 114 | $this->assertEquals($expectedContent, $content); 115 | } 116 | 117 | public function testGenerateReturnsSourceFilePaths() 118 | { 119 | $res = $this->object->generate(); 120 | $this->assertInternalType('array', $res[0]->sourceFiles); 121 | $this->assertCount(3, $res[0]->sourceFiles); 122 | $this->assertFileExists($res[0]->sourceFiles[0]); 123 | } 124 | 125 | public function testFilters() 126 | { 127 | $filter = function ($code, \WebLoader\Compiler $loader) { 128 | return $code . $code; 129 | }; 130 | $this->object->addFilter($filter); 131 | $this->object->addFilter($filter); 132 | $this->assertEquals(array($filter, $filter), $this->object->getFilters()); 133 | } 134 | 135 | public function testFileFilters() 136 | { 137 | $filter = function ($code, \WebLoader\Compiler $loader, $file = null) { 138 | return $code . $code; 139 | }; 140 | $this->object->addFileFilter($filter); 141 | $this->object->addFileFilter($filter); 142 | $this->assertEquals(array($filter, $filter), $this->object->getFileFilters()); 143 | } 144 | 145 | /** 146 | * @expectedException \WebLoader\InvalidArgumentException 147 | */ 148 | public function testNonCallableFilter() 149 | { 150 | $this->object->addFilter(4); 151 | } 152 | 153 | /** 154 | * @expectedException \WebLoader\InvalidArgumentException 155 | */ 156 | public function testNonCallableFileFilter() 157 | { 158 | $this->object->addFileFilter(4); 159 | } 160 | 161 | } 162 | -------------------------------------------------------------------------------- /tests/DefaultOutputNamingConventionTest.php: -------------------------------------------------------------------------------- 1 | object = new DefaultOutputNamingConvention(); 23 | $this->compiler = \Mockery::mock('Webloader\Compiler'); 24 | } 25 | 26 | public function testMultipleFiles() 27 | { 28 | $files = array( 29 | __DIR__ . DIRECTORY_SEPARATOR . 'fixtures' . DIRECTORY_SEPARATOR . 'a.txt', 30 | __DIR__ . DIRECTORY_SEPARATOR . 'fixtures' . DIRECTORY_SEPARATOR . 'b.txt', 31 | ); 32 | 33 | $name = $this->object->getFilename($files, $this->compiler); 34 | $this->assertRegExp('/^webloader-[0-9a-f]{12}$/', $name); 35 | 36 | // another hash 37 | $files[] = __DIR__ . DIRECTORY_SEPARATOR . 'fixtures' . DIRECTORY_SEPARATOR . 'c.txt'; 38 | $name2 = $this->object->getFilename($files, $this->compiler); 39 | $this->assertNotEquals($name, $name2, 'Different file lists results to same filename.'); 40 | } 41 | 42 | public function testOneFile() 43 | { 44 | $files = array( 45 | __DIR__ . DIRECTORY_SEPARATOR . 'fixtures' . DIRECTORY_SEPARATOR . 'a.txt', 46 | ); 47 | 48 | $name = $this->object->getFilename($files, $this->compiler); 49 | $this->assertRegExp('/^webloader-[0-9a-f]{12}-a$/', $name); 50 | } 51 | 52 | public function testCssConvention() 53 | { 54 | $files = array( 55 | __DIR__ . DIRECTORY_SEPARATOR . 'fixtures' . DIRECTORY_SEPARATOR . 'a.txt', 56 | ); 57 | 58 | $name = DefaultOutputNamingConvention::createCssConvention()->getFilename($files, $this->compiler); 59 | $this->assertRegExp('/^cssloader-[0-9a-f]{12}-a.css$/', $name); 60 | } 61 | 62 | public function testJsConvention() 63 | { 64 | $files = array( 65 | __DIR__ . DIRECTORY_SEPARATOR . 'fixtures' . DIRECTORY_SEPARATOR . 'a.txt', 66 | ); 67 | 68 | $name = DefaultOutputNamingConvention::createJsConvention()->getFilename($files, $this->compiler); 69 | $this->assertRegExp('/^jsloader-[0-9a-f]{12}-a.js$/', $name); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /tests/FileCollectionTest.php: -------------------------------------------------------------------------------- 1 | object = new FileCollection(__DIR__ . '/fixtures'); 21 | } 22 | 23 | public function testAddGetFiles() 24 | { 25 | $this->object->addFile('a.txt'); 26 | $this->object->addFile(__DIR__ . '/fixtures/a.txt'); 27 | $this->object->addFile(__DIR__ . '/fixtures/b.txt'); 28 | $this->object->addFiles(array(__DIR__ . '/fixtures/c.txt')); 29 | $expected = array( 30 | __DIR__ . DIRECTORY_SEPARATOR . 'fixtures' . DIRECTORY_SEPARATOR . 'a.txt', 31 | __DIR__ . DIRECTORY_SEPARATOR . 'fixtures' . DIRECTORY_SEPARATOR . 'b.txt', 32 | __DIR__ . DIRECTORY_SEPARATOR . 'fixtures' . DIRECTORY_SEPARATOR . 'c.txt', 33 | ); 34 | $this->assertEqualPaths($expected, $this->object->getFiles()); 35 | } 36 | 37 | /** 38 | * @expectedException \Webloader\FileNotFoundException 39 | */ 40 | public function testAddNonExistingFile() 41 | { 42 | $this->object->addFile('sdfsdg.txt'); 43 | } 44 | 45 | public function testRemoveFile() 46 | { 47 | $this->object->addFile(__DIR__ . '/fixtures/a.txt'); 48 | $this->object->addFile(__DIR__ . '/fixtures/b.txt'); 49 | 50 | $this->object->removeFile('a.txt'); 51 | $expected = array( 52 | __DIR__ . DIRECTORY_SEPARATOR . 'fixtures' . DIRECTORY_SEPARATOR . 'b.txt', 53 | ); 54 | $this->assertEqualPaths($expected, $this->object->getFiles()); 55 | 56 | $this->object->removeFiles(array(__DIR__ . '/fixtures/b.txt')); 57 | } 58 | 59 | public function testCannonicalizePath() 60 | { 61 | $abs = __DIR__ . '/./fixtures/a.txt'; 62 | $rel = 'a.txt'; 63 | $expected = __DIR__ . DIRECTORY_SEPARATOR . 'fixtures' . DIRECTORY_SEPARATOR . 'a.txt'; 64 | 65 | $this->assertEqualPaths($expected, $this->object->cannonicalizePath($abs)); 66 | $this->assertEqualPaths($expected, $this->object->cannonicalizePath($rel)); 67 | 68 | try { 69 | $this->object->cannonicalizePath('nesdagf'); 70 | $this->fail('Exception was not thrown.'); 71 | } catch (\WebLoader\FileNotFoundException $e) { 72 | } 73 | } 74 | 75 | public function testClear() 76 | { 77 | $this->object->addFile('a.txt'); 78 | $this->object->addRemoteFile('http://jquery.com/jquery.js'); 79 | $this->object->addWatchFile('b.txt'); 80 | $this->object->clear(); 81 | 82 | $this->assertEquals(array(), $this->object->getFiles()); 83 | $this->assertEquals(array(), $this->object->getRemoteFiles()); 84 | $this->assertEquals(array(), $this->object->getWatchFiles()); 85 | } 86 | 87 | public function testRemoteFiles() 88 | { 89 | $this->object->addRemoteFile('http://jquery.com/jquery.js'); 90 | $this->object->addRemoteFiles(array( 91 | 'http://jquery.com/jquery.js', 92 | 'http://google.com/angular.js', 93 | )); 94 | 95 | $expected = array( 96 | 'http://jquery.com/jquery.js', 97 | 'http://google.com/angular.js', 98 | ); 99 | $this->assertEquals($expected, $this->object->getRemoteFiles()); 100 | } 101 | 102 | public function testWatchFiles() 103 | { 104 | $this->object->addWatchFile(__DIR__ . '/fixtures/a.txt'); 105 | $this->object->addWatchFile(__DIR__ . '/fixtures/b.txt'); 106 | $this->object->addWatchFiles(array(__DIR__ . '/fixtures/c.txt')); 107 | $expected = array( 108 | __DIR__ . DIRECTORY_SEPARATOR . 'fixtures' . DIRECTORY_SEPARATOR . 'a.txt', 109 | __DIR__ . DIRECTORY_SEPARATOR . 'fixtures' . DIRECTORY_SEPARATOR . 'b.txt', 110 | __DIR__ . DIRECTORY_SEPARATOR . 'fixtures' . DIRECTORY_SEPARATOR . 'c.txt', 111 | ); 112 | $this->assertEqualPaths($expected, $this->object->getWatchFiles()); 113 | } 114 | 115 | public function testTraversableFiles() 116 | { 117 | $this->object->addFiles(new \ArrayIterator(array('a.txt'))); 118 | $this->assertEquals(1, count($this->object->getFiles())); 119 | } 120 | 121 | public function testTraversableRemoteFiles() 122 | { 123 | $this->object->addRemoteFiles(new \ArrayIterator(array('http://jquery.com/jquery.js'))); 124 | $this->assertEquals(1, count($this->object->getRemoteFiles())); 125 | } 126 | 127 | public function testSplFileInfo() 128 | { 129 | $this->object->addFile(new \SplFileInfo(__DIR__ . '/fixtures/a.txt')); 130 | $this->assertEquals(1, count($this->object->getFiles())); 131 | } 132 | 133 | private function assertEqualPaths($expected, $actual) 134 | { 135 | $actual = (array) $actual; 136 | foreach ((array) $expected as $key => $path) { 137 | $this->assertTrue(isset($actual[$key])); 138 | $this->assertEquals(\WebLoader\Path::normalize($path), \WebLoader\Path::normalize($actual[$key])); 139 | } 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /tests/Filter/CssUrlsFilterTest.php: -------------------------------------------------------------------------------- 1 | object = new CssUrlsFilter(__DIR__ . '/..', '/'); 16 | } 17 | 18 | public function testCannonicalizePath() 19 | { 20 | $path = $this->object->cannonicalizePath('/prase/./dobytek/../ale/nic.jpg'); 21 | $this->assertEquals('/prase/ale/nic.jpg', $path); 22 | } 23 | 24 | public function testAbsolutizeAbsolutized() 25 | { 26 | $cssPath = __DIR__ . '/../fixtures/style.css'; 27 | 28 | $url = 'http://image.com/image.jpg'; 29 | $this->assertEquals($url, $this->object->absolutizeUrl($url, '\'', $cssPath)); 30 | 31 | $abs = '/images/img.png'; 32 | $this->assertEquals($abs, $this->object->absolutizeUrl($abs, '\'', $cssPath)); 33 | } 34 | 35 | public function testAbsolutize() 36 | { 37 | $cssPath = __DIR__ . '/../fixtures/style.css'; 38 | 39 | $this->assertEquals( 40 | '/images/image.png', 41 | $this->object->absolutizeUrl('./../images/image.png', '\'', $cssPath) 42 | ); 43 | 44 | $this->assertEquals( 45 | '/images/path/to/image.png', 46 | $this->object->absolutizeUrl('./../images/path/./to/image.png', '\'', $cssPath) 47 | ); 48 | } 49 | 50 | public function testAbsolutizeOutsideOfDocRoot() 51 | { 52 | $path = './../images/image.png'; 53 | $existingPath = __DIR__ . '/../../Compiler.php'; 54 | $this->assertEquals($path, $this->object->absolutizeUrl($path, '\'', $existingPath)); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /tests/Filter/LessFilterTest.php: -------------------------------------------------------------------------------- 1 | filter = new LessFilter(new \lessc()); 21 | 22 | $files = new FileCollection(__DIR__ . '/../fixtures'); 23 | @mkdir($outputDir = __DIR__ . '/../temp/'); 24 | $this->compiler = new Compiler($files, new DefaultOutputNamingConvention(), $outputDir); 25 | } 26 | 27 | public function testReplace() 28 | { 29 | $file = __DIR__ . '/../fixtures/style.less'; 30 | $less = $this->filter->__invoke(file_get_contents($file), $this->compiler, $file); 31 | $this->assertSame(file_get_contents(__DIR__ . '/../fixtures/style.less.expected'), $less); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /tests/Filter/PHPCoffeeScriptFilterTest.php: -------------------------------------------------------------------------------- 1 | object = new PHPCoffeeScriptFilter(); 21 | } 22 | 23 | public function testSimpleLoadAndParse() 24 | { 25 | if (!class_exists('CoffeeScript\Compiler')) { 26 | $this->markTestSkipped('Missing CoffeeScript compiler.'); 27 | } 28 | 29 | $compiler = new PHPCoffeeScriptFilter(); 30 | $coffee = $compiler->compileCoffee("number = -42 if opposite", null); 31 | 32 | $version = COFFEESCRIPT_VERSION; 33 | $expected = <<assertEquals($expected, $coffee); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/Filter/ScssFilterTest.php: -------------------------------------------------------------------------------- 1 | filter = new ScssFilter(new \Leafo\ScssPhp\Compiler()); 22 | 23 | $files = new FileCollection(__DIR__ . '/../fixtures'); 24 | @mkdir($outputDir = __DIR__ . '/../temp/'); 25 | $this->compiler = new Compiler($files, new DefaultOutputNamingConvention(), $outputDir); 26 | } 27 | 28 | public function testReplace() 29 | { 30 | $file = __DIR__ . '/../fixtures/style.scss'; 31 | $less = $this->filter->__invoke(file_get_contents($file), $this->compiler, $file); 32 | $this->assertSame(file_get_contents(__DIR__ . '/../fixtures/style.scss.expected'), $less); 33 | } 34 | 35 | public function testImportAbsolutePath() 36 | { 37 | $file = __DIR__ . '/../fixtures/styleAbsolute.scss'; 38 | $filter = new VariablesFilter(array( 39 | 'fixturesAbsolutePath' => realpath(__DIR__.'/../fixtures'), 40 | )); 41 | $code = file_get_contents($file); 42 | $filtered = $filter($code); 43 | $less = $this->filter->__invoke($filtered, $this->compiler, $file); 44 | $this->assertSame(file_get_contents(__DIR__ . '/../fixtures/styleAbsolute.scss.expected'), $less); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /tests/Filter/VariablesFilterTest.php: -------------------------------------------------------------------------------- 1 | object = new VariablesFilter(array( 16 | 'foo' => 'bar', 17 | )); 18 | } 19 | 20 | public function testReplace() 21 | { 22 | $this->object->bar = 'baz'; 23 | 24 | $filter = $this->object; 25 | 26 | $code = 'a tak sel {{$foo}} za {{$bar}}em a potkali druheho {{$foo}}'; 27 | 28 | $filtered = $filter($code); 29 | 30 | $this->assertEquals('a tak sel bar za bazem a potkali druheho bar', $filtered); 31 | } 32 | 33 | public function testDelimiters() 34 | { 35 | $this->object->setDelimiter('[', ']'); 36 | $this->assertEquals('bar', call_user_func($this->object, '[foo]')); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /tests/Nette/ExtensionTest.php: -------------------------------------------------------------------------------- 1 | exclude('.gitignore')->from($tempDir . '/cache') as $file) { 17 | unlink((string) $file); 18 | } 19 | 20 | $configurator = new \Nette\Configurator(); 21 | $configurator->setTempDirectory($tempDir); 22 | 23 | foreach ($configFiles as $file) { 24 | $configurator->addConfig($file, FALSE); 25 | } 26 | 27 | $configurator->addParameters(array( 28 | 'wwwDir' => __DIR__ . '/..', 29 | 'fixturesDir' => __DIR__ . '/../fixtures', 30 | 'tempDir' => $tempDir, 31 | )); 32 | 33 | $extension = new \WebLoader\Nette\Extension(); 34 | $extension->install($configurator); 35 | 36 | $this->container = @$configurator->createContainer(); // sends header X-Powered-By, ... 37 | } 38 | 39 | public function testJsCompilerService() 40 | { 41 | $this->prepareContainer(array(__DIR__ . '/../fixtures/extension.neon')); 42 | $this->assertInstanceOf('WebLoader\Compiler', $this->container->getService('webloader.jsDefaultCompiler')); 43 | } 44 | 45 | public function testExcludeFiles() 46 | { 47 | $this->prepareContainer(array(__DIR__ . '/../fixtures/extension.neon')); 48 | $files = $this->container->getService('webloader.jsExcludeCompiler')->getFileCollection()->getFiles(); 49 | 50 | $this->assertTrue(in_array(realpath(__DIR__ . '/../fixtures/a.txt'), $files)); 51 | $this->assertFalse(in_array(realpath(__DIR__ . '/../fixtures/dir/one.js'), $files)); 52 | } 53 | 54 | public function testJoinFilesOn() 55 | { 56 | $this->prepareContainer(array( 57 | __DIR__ . '/../fixtures/extension.neon', 58 | __DIR__ . '/../fixtures/extensionJoinFilesTrue.neon', 59 | )); 60 | $this->assertTrue($this->container->getService('webloader.jsDefaultCompiler')->getJoinFiles()); 61 | } 62 | 63 | public function testJoinFilesOff() 64 | { 65 | $this->prepareContainer(array( 66 | __DIR__ . '/../fixtures/extension.neon', 67 | __DIR__ . '/../fixtures/extensionJoinFilesFalse.neon', 68 | )); 69 | $this->assertFalse($this->container->getService('webloader.jsDefaultCompiler')->getJoinFiles()); 70 | } 71 | 72 | public function testJoinFilesOffInOneService() 73 | { 74 | $this->prepareContainer(array( 75 | __DIR__ . '/../fixtures/extension.neon', 76 | )); 77 | $this->assertFalse($this->container->getService('webloader.cssJoinOffCompiler')->getJoinFiles()); 78 | } 79 | 80 | public function testExtensionName() 81 | { 82 | $tempDir = __DIR__ . '/../temp'; 83 | $class = 'ExtensionNameServiceContainer'; 84 | 85 | $configurator = new \Nette\Configurator(); 86 | $configurator->setTempDirectory($tempDir); 87 | $configurator->addParameters(array('container' => array('class' => $class))); 88 | $configurator->onCompile[] = function ($configurator, \Nette\DI\Compiler $compiler) { 89 | $compiler->addExtension('Foo', new \WebLoader\Nette\Extension()); 90 | }; 91 | $configurator->addConfig(__DIR__ . '/../fixtures/extensionName.neon', false); 92 | $container = $configurator->createContainer(); 93 | 94 | $this->assertInstanceOf('WebLoader\Compiler', $container->getService('Foo.cssDefaultCompiler')); 95 | $this->assertInstanceOf('WebLoader\Compiler', $container->getService('Foo.jsDefaultCompiler')); 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /tests/Path/PathTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('/path/to/project/that/contains/0/in/it', $normalized); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | div { 10 | float: left; 11 | width: 610px; 12 | height: 194px; 13 | background: @bgBanner; 14 | margin: 20px 0 0 20px; 15 | position: relative; 16 | 17 | h3 { 18 | width: auto; 19 | color: @textRed; 20 | font-weight: 600; 21 | padding: 10px 20px; 22 | margin: 10px 10px 0 10px; 23 | text-shadow: 0 2px 0 rgba(0, 0, 0, .3); 24 | display: inline-block; 25 | font-size: 24px; 26 | } 27 | 28 | h3, p { 29 | color: @white; 30 | } 31 | p { 32 | font-size: 13px; 33 | max-width: 360px; 34 | padding: 10px; 35 | 36 | strong { 37 | font-weight: 600; 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/fixtures/style.less.expected: -------------------------------------------------------------------------------- 1 | .clearFix { 2 | display: block; 3 | zoom: 1; 4 | } 5 | .clearFix:after { 6 | content: " "; 7 | display: block; 8 | font-size: 0; 9 | height: 0; 10 | clear: both; 11 | visibility: hidden; 12 | } 13 | div.banners { 14 | display: block; 15 | zoom: 1; 16 | padding: 0 0 20px; 17 | margin: 0 10px; 18 | border-bottom: #f4f4f4 1px solid; 19 | } 20 | div.banners:after { 21 | content: " "; 22 | display: block; 23 | font-size: 0; 24 | height: 0; 25 | clear: both; 26 | visibility: hidden; 27 | } 28 | div.banners > div { 29 | float: left; 30 | width: 610px; 31 | height: 194px; 32 | background: #f9f2e8; 33 | margin: 20px 0 0 20px; 34 | position: relative; 35 | } 36 | div.banners > div h3 { 37 | width: auto; 38 | color: #be2025; 39 | font-weight: 600; 40 | padding: 10px 20px; 41 | margin: 10px 10px 0 10px; 42 | text-shadow: 0 2px 0 rgba(0, 0, 0, 0.3); 43 | display: inline-block; 44 | font-size: 24px; 45 | } 46 | div.banners > div h3, 47 | div.banners > div p { 48 | color: #ffffff; 49 | } 50 | div.banners > div p { 51 | font-size: 13px; 52 | max-width: 360px; 53 | padding: 10px; 54 | } 55 | div.banners > div p strong { 56 | font-weight: 600; 57 | } 58 | -------------------------------------------------------------------------------- /tests/fixtures/style.scss: -------------------------------------------------------------------------------- 1 | 2 | @import 'style2.scss'; 3 | 4 | .navigation { 5 | ul { 6 | line-height: 20px; 7 | color: blue; 8 | a { 9 | color: red; 10 | } 11 | } 12 | } 13 | 14 | .footer { 15 | .copyright { 16 | color: silver; 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /tests/fixtures/style.scss.expected: -------------------------------------------------------------------------------- 1 | .clearFix { 2 | display: block; 3 | zoom: 1; } 4 | 5 | .navigation ul { 6 | line-height: 20px; 7 | color: blue; } 8 | .navigation ul a { 9 | color: red; } 10 | 11 | .footer .copyright { 12 | color: silver; } 13 | -------------------------------------------------------------------------------- /tests/fixtures/style2.less: -------------------------------------------------------------------------------- 1 | 2 | @borderLight: #f4f4f4; 3 | @bgBanner: #f9f2e8; 4 | @textRed: #be2025; 5 | @white: #ffffff; 6 | 7 | 8 | .clearFix { 9 | display: block; 10 | zoom: 1; 11 | 12 | &:after { 13 | content: " "; 14 | display: block; 15 | font-size: 0; 16 | height: 0; 17 | clear: both; 18 | visibility: hidden; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/fixtures/style2.scss: -------------------------------------------------------------------------------- 1 | .clearFix { 2 | display: block; 3 | zoom: 1; 4 | } 5 | -------------------------------------------------------------------------------- /tests/fixtures/styleAbsolute.scss: -------------------------------------------------------------------------------- 1 | 2 | @import '{{$fixturesAbsolutePath}}/style2.scss'; 3 | 4 | .navigation { 5 | ul { 6 | line-height: 20px; 7 | color: blue; 8 | a { 9 | color: red; 10 | } 11 | } 12 | } 13 | 14 | .footer { 15 | .copyright { 16 | color: silver; 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /tests/fixtures/styleAbsolute.scss.expected: -------------------------------------------------------------------------------- 1 | .clearFix { 2 | display: block; 3 | zoom: 1; } 4 | 5 | .navigation ul { 6 | line-height: 20px; 7 | color: blue; } 8 | .navigation ul a { 9 | color: red; } 10 | 11 | .footer .copyright { 12 | color: silver; } 13 | -------------------------------------------------------------------------------- /tests/phpunit.xml: -------------------------------------------------------------------------------- 1 | 12 | 13 | -------------------------------------------------------------------------------- /tests/temp/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.* 3 | !cache -------------------------------------------------------------------------------- /tests/temp/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.* -------------------------------------------------------------------------------- /tests/travis/composer-nette-2.3.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "janmarek/webloader", 3 | "description": "Tool for loading or deploying CSS and JS files into web pages", 4 | "keywords": ["webloader", "css", "javascript", "assets", "nette"], 5 | "homepage": "http://addons.nette.org/cs/webloader", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Jan Marek", 10 | "email": "mail@janmarek.net" 11 | } 12 | ], 13 | "autoload": { 14 | "psr-0": { 15 | "WebLoader": "" 16 | } 17 | }, 18 | "require": { 19 | "nette/application": "2.3.*@rc", 20 | "nette/di": "2.3.*@rc", 21 | "nette/utils": "2.3.*@rc" 22 | }, 23 | "suggest": { 24 | "oyejorge/less.php": "Lessphp is a composer for LESS written in PHP.", 25 | "leafo/lessphp": "Lessphp is a composer for LESS written in PHP." 26 | }, 27 | "require-dev": { 28 | "nette/application": "2.3.*@rc", 29 | "nette/bootstrap": "2.3.*@rc", 30 | "nette/caching": "2.3.*@rc", 31 | "nette/component-model": "~2.2@rc", 32 | "nette/database": "2.3.*@rc", 33 | "nette/deprecated": "~2.2@rc", 34 | "nette/di": "2.3.*@rc", 35 | "nette/finder": "2.3.*@rc", 36 | "nette/forms": "2.3.*@rc", 37 | "nette/http": "2.3.*@rc", 38 | "nette/mail": "2.3.*@rc", 39 | "nette/neon": "2.3.*@rc", 40 | "nette/php-generator": "2.3.*@rc", 41 | "nette/reflection": "2.3.*@rc", 42 | "nette/robot-loader": "2.3.*@rc", 43 | "nette/safe-stream": "2.3.*@rc", 44 | "nette/security": "2.3.*@rc", 45 | "nette/tokenizer": "~2.2@rc", 46 | "nette/utils": "2.3.*@rc", 47 | "latte/latte": "2.3.*@rc", 48 | "tracy/tracy": "2.3.*@rc", 49 | 50 | "oyejorge/less.php": "~1.5", 51 | "leafo/scssphp": "~0.1", 52 | 53 | "mockery/mockery": "0.7.*", 54 | "phpunit/phpunit": "3.7.*", 55 | "jakub-onderka/php-parallel-lint": "~0.7" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tests/travis/prepare-composer.php: -------------------------------------------------------------------------------- 1 |