├── .gitignore ├── .travis.yml ├── composer.json ├── phpunit.xml ├── readme.md ├── src ├── Barryvdh │ └── Assetic │ │ ├── AsseticServiceProvider.php │ │ ├── CheckedAssetWriter.php │ │ ├── Console │ │ └── AsseticBuildCommand.php │ │ └── Dumper.php └── config │ ├── .gitkeep │ └── config.php └── tests └── .gitkeep /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.phar 3 | composer.lock 4 | .DS_Store -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.3 5 | - 5.4 6 | - 5.5 7 | 8 | before_script: 9 | - curl -s http://getcomposer.org/installer | php 10 | - php composer.phar install --dev 11 | 12 | script: phpunit -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "barryvdh/laravel-assetic", 3 | "description": "", 4 | "authors": [ 5 | { 6 | "name": "Barry vd. Heuvel", 7 | "email": "barryvdh@gmail.com" 8 | } 9 | ], 10 | "require": { 11 | "php": ">=5.3.0", 12 | "illuminate/support": "4.x", 13 | "kriswallsmith/assetic": "~1.2", 14 | "symfony/finder": "~2.3" 15 | }, 16 | "suggest": { 17 | "twig/twig": "~1.14" 18 | }, 19 | "autoload": { 20 | "psr-0": { 21 | "Barryvdh\\Assetic": "src/" 22 | } 23 | }, 24 | "extra": { 25 | "branch-alias": { 26 | "dev-master": "0.1-dev" 27 | } 28 | }, 29 | "minimum-stability": "dev" 30 | } 31 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | ./tests/ 16 | 17 | 18 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## Assetic for Laravel 2 | 3 | A ServiceProvider based on https://github.com/mheap/Silex-Assetic 4 | 5 | Install via composer, add the ServiceProvider and configure assets/filters in the config. 6 | 7 | Add this package to composer.json 8 | 9 | "require": { 10 | .. 11 | "barryvdh/laravel-assetic": "0.1.x" 12 | } 13 | 14 | And run `composer update`. If you get the error, **barryvdh/laravel-assetic dev-master requires kriswallsmith/assetic ~1.2 -> no matching package found**, you might need to add or change your composer.json settings to the following: 15 | 16 | { 17 | 18 | ... 19 | "minimum-stability": "dev", 20 | "prefer-stable": true 21 | 22 | } 23 | 24 | 25 | Then add the ServiceProvider to the providers array in app/config/app.php 26 | 27 | 'providers' => array( 28 | .. 29 | 'Barryvdh\Assetic\AsseticServiceProvider', 30 | ) 31 | 32 | Finally publish the config file (`php artisan config:publish barryvdh/laravel-assetic`) and add your filters to the config. 33 | 34 | // app/config/packages/barryvdh/laravel-assetic/config.php 35 | 'filter_manager' => function(FilterManager $fm){ 36 | $fm->set('less', new \Assetic\Filter\LessphpFilter()); 37 | $fm->set('cssmin', new Assetic\Filter\CssMinFilter); 38 | $fm->set('jsmin', new Assetic\Filter\JSMinFilter); 39 | $fm->set('cssrewrite', new Assetic\Filter\CssRewriteFilter()); 40 | }, 41 | 42 | 43 | When Twig is installed, the Assetic Extension can be used. Be sure to include the AsseticServiceProvider AFTER the TwigServiceProvider 44 | -------------------------------------------------------------------------------- /src/Barryvdh/Assetic/AsseticServiceProvider.php: -------------------------------------------------------------------------------- 1 | app; 40 | 41 | /** 42 | * Asset Factory configuration happens here 43 | */ 44 | $app['assetic'] = $app->share(function () use ($app) { 45 | $app['assetic.path_to_web'] = $app['config']->get('laravel-assetic::config.path_to_web'); 46 | if( $app['config']->has('laravel-assetic::config.path_to_source')){ 47 | $app['assetic.path_to_source'] = $app['config']->get('laravel-assetic::config.path_to_source'); 48 | } 49 | $app['assetic.options'] = $app['config']->get('laravel-assetic::config.options'); 50 | 51 | // initializing lazy asset manager 52 | if (isset($app['assetic.formulae']) && 53 | !is_array($app['assetic.formulae']) && 54 | !empty($app['assetic.formulae']) 55 | ) { 56 | $app['assetic.lazy_asset_manager']; 57 | } 58 | 59 | return $app['assetic.factory']; 60 | }); 61 | 62 | /** 63 | * Factory 64 | * 65 | * @return \Assetic\Factory\AssetFactory 66 | */ 67 | $app['assetic.factory'] = $app->share(function () use ($app) { 68 | $root = isset($app['assetic.path_to_source']) ? $app['assetic.path_to_source'] : $app['assetic.path_to_web']; 69 | $factory = new AssetFactory($root, $app['assetic.options']['debug']); 70 | $factory->setAssetManager($app['assetic.asset_manager']); 71 | $factory->setFilterManager($app['assetic.filter_manager']); 72 | 73 | 74 | if($app['config']->get('laravel-assetic::config.cachebusting') and ! $app['assetic.options']['debug'] ){ 75 | $factory->addWorker(new CacheBustingWorker()); 76 | } 77 | 78 | 79 | return $factory; 80 | }); 81 | 82 | /** 83 | * Asset writer, writes to the 'assetic.path_to_web' folder 84 | * 85 | * @return \Assetic\AssetWriter 86 | */ 87 | $app['assetic.asset_writer'] = $app->share(function () use ($app) { 88 | return new CheckedAssetWriter($app['assetic.path_to_web']); 89 | }); 90 | 91 | /** 92 | * Asset manager 93 | * 94 | * @return \Assetic\AssetManager 95 | */ 96 | $app['assetic.asset_manager'] = $app->share(function () use ($app) { 97 | $am = new AssetManager(); 98 | if($app['config']->has('laravel-assetic::config.asset_manager')){ 99 | $callback = $app['config']->get('laravel-assetic::config.asset_manager'); 100 | if(is_callable($callback)){ 101 | $callback($am); 102 | } 103 | } 104 | return $am; 105 | }); 106 | 107 | /** 108 | * Filter manager 109 | * 110 | * @return \Assetic\FilterManager 111 | */ 112 | $app['assetic.filter_manager'] = $app->share(function () use ($app) { 113 | $fm = new FilterManager(); 114 | 115 | if($app['config']->has('laravel-assetic::config.filter_manager')){ 116 | $callback = $app['config']->get('laravel-assetic::config.filter_manager'); 117 | if(is_callable($callback)){ 118 | $callback($fm); 119 | } 120 | } 121 | 122 | return $fm; 123 | }); 124 | 125 | /** 126 | * Lazy asset manager for loading assets from $app['assetic.formulae'] 127 | * (will be later maybe removed) 128 | */ 129 | $app['assetic.lazy_asset_manager'] = $app->share(function () use ($app) { 130 | $formulae = isset($app['assetic.formulae']) ? $app['assetic.formulae'] : array(); 131 | $options = $app['assetic.options']; 132 | $lazy = new LazyAssetmanager($app['assetic.factory']); 133 | 134 | if (empty($formulae)) { 135 | return $lazy; 136 | } 137 | 138 | foreach ($formulae as $name => $formula) { 139 | $lazy->setFormula($name, $formula); 140 | } 141 | 142 | if ($options['formulae_cache_dir'] !== null && $options['debug'] !== true) { 143 | foreach ($lazy->getNames() as $name) { 144 | $lazy->set($name, new AssetCache( 145 | $lazy->get($name), 146 | new FilesystemCache($options['formulae_cache_dir']) 147 | )); 148 | } 149 | } 150 | 151 | return $lazy; 152 | }); 153 | 154 | $app['assetic.dumper'] = $app->share(function () use ($app) { 155 | return new Dumper( 156 | $app['assetic.asset_manager'], 157 | $app['assetic.lazy_asset_manager'], 158 | $app['assetic.asset_writer'], 159 | $app['view']->getFinder() 160 | ); 161 | }); 162 | 163 | $app['command.assetic.build'] = $app->share(function($app) 164 | { 165 | return new Console\AsseticBuildCommand(); 166 | }); 167 | $this->commands('command.assetic.build'); 168 | } 169 | 170 | public function boot(){ 171 | $app = $this->app; 172 | $this->package('barryvdh/laravel-assetic'); 173 | 174 | // Register our filters to use 175 | if (isset($app['assetic.filters']) && is_callable($app['assetic.filters'])) { 176 | $app['assetic.filters']($app['assetic.filter_manager']); 177 | } 178 | 179 | if (isset($app['twig'])) { 180 | $app['twig']->addExtension(new AsseticExtension($app['assetic'])); 181 | 182 | $app->extend('assetic.lazy_asset_manager', function ($am, $app) { 183 | $am->setLoader('twig', new TwigFormulaLoader($app['twig'])); 184 | return $am; 185 | }); 186 | 187 | $app->extend('assetic.dumper', function ($helper, $app) { 188 | $helper->setTwig($app['twig'], $app['twig.loader']); 189 | return $helper; 190 | }); 191 | } 192 | 193 | /** 194 | * Writes down all lazy asset manager and asset managers assets 195 | */ 196 | $app->after(function () use ($app) { 197 | // Boot assetic 198 | $assetic = $app['assetic']; 199 | 200 | if (!isset($app['assetic.options']['auto_dump_assets']) || 201 | !$app['assetic.options']['auto_dump_assets']) { 202 | return; 203 | } 204 | $helper = $app['assetic.dumper']; 205 | if (isset($app['twig'])) { 206 | $helper->addTwigAssets(); 207 | } 208 | $helper->dumpAssets(); 209 | }); 210 | } 211 | 212 | /** 213 | * Get the services provided by the provider. 214 | * 215 | * @return array 216 | */ 217 | public function provides() 218 | { 219 | return array('assetic', 'assetic.factory', 'assetic.dumper', 'assetic.filters', 220 | 'assetic.asset_manager', 'assetic.filtermanager', 'assetic.lazy_asset_manager', 221 | 'assetic.asset_writer', 'command.assetic.build'); 222 | } 223 | 224 | } 225 | -------------------------------------------------------------------------------- /src/Barryvdh/Assetic/CheckedAssetWriter.php: -------------------------------------------------------------------------------- 1 | 13 | * @author Johannes M. Schmitt 14 | */ 15 | class CheckedAssetWriter extends AssetWriter 16 | { 17 | private $dir; 18 | private $values; 19 | 20 | /** 21 | * Constructor. 22 | * 23 | * @param string $dir The base web directory 24 | * @param array $values Variable values 25 | * 26 | * @throws \InvalidArgumentException if a variable value is not a string 27 | */ 28 | public function __construct($dir, array $values = array()) 29 | { 30 | foreach ($values as $var => $vals) { 31 | foreach ($vals as $value) { 32 | if (!is_string($value)) { 33 | throw new \InvalidArgumentException(sprintf('All variable values must be strings, but got %s for variable "%s".', json_encode($value), $var)); 34 | } 35 | } 36 | } 37 | 38 | $this->dir = $dir; 39 | $this->values = $values; 40 | } 41 | 42 | 43 | public function writeAsset(AssetInterface $asset) 44 | { 45 | foreach (VarUtils::getCombinations($asset->getVars(), $this->values) as $combination) { 46 | $asset->setValues($combination); 47 | 48 | $path = $this->dir.'/'.VarUtils::resolve( 49 | $asset->getTargetPath(), 50 | $asset->getVars(), 51 | $asset->getValues() 52 | ); 53 | 54 | if (!is_dir($path) && (!file_exists($path) || filemtime($path) <= $asset->getLastModified())){ 55 | static::write( 56 | $path, 57 | $asset->dump() 58 | ); 59 | } 60 | } 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/Barryvdh/Assetic/Console/AsseticBuildCommand.php: -------------------------------------------------------------------------------- 1 | info('Starting Assetic building..'); 45 | $app = $this->laravel; 46 | 47 | // Boot assetic 48 | $assetic = $app['assetic']; 49 | 50 | $helper = $app['assetic.dumper']; 51 | if (isset($app['twig'])) { 52 | $helper->addTwigAssets(); 53 | } 54 | $helper->dumpAssets(); 55 | 56 | $this->info('Done building assets!'); 57 | 58 | } 59 | 60 | 61 | 62 | /** 63 | * Get the console command arguments. 64 | * 65 | * @return array 66 | */ 67 | protected function getArguments() 68 | { 69 | return array(); 70 | } 71 | 72 | /** 73 | * Get the console command options. 74 | * 75 | * @return array 76 | */ 77 | protected function getOptions() 78 | { 79 | return array(); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/Barryvdh/Assetic/Dumper.php: -------------------------------------------------------------------------------- 1 | viewfinder = $viewfinder; 58 | $this->am = $am; 59 | $this->lam = $lam; 60 | $this->writer = $writer; 61 | 62 | } 63 | 64 | /** 65 | * @param \Twig_Environment $twig 66 | * @param \Twig_Loader_Filesystem $loader 67 | */ 68 | public function setTwig(\Twig_Environment $twig, \Twig_LoaderInterface $loader) 69 | { 70 | $this->twig = $twig; 71 | $this->loader = $loader; 72 | } 73 | 74 | /** 75 | * Locates twig templates and adds their defined assets to the lazy asset manager 76 | */ 77 | public function addTwigAssets() 78 | { 79 | if (!$this->twig instanceof \Twig_Environment) { 80 | throw new \LogicException('Twig environment not set'); 81 | } 82 | 83 | $finder = new Finder(); 84 | $viewfinder = $this->viewfinder; 85 | 86 | if ( count($viewfinder->getPaths()) > 0 ) { 87 | $iterator = $finder->files()->in($viewfinder->getPaths()); 88 | 89 | foreach ($iterator as $file) { 90 | $resource = new TwigResource($this->loader, $file->getRelativePathname()); 91 | $this->lam->addResource($resource, 'twig'); 92 | } 93 | } 94 | } 95 | 96 | /** 97 | * Dumps all the assets 98 | */ 99 | public function dumpAssets() 100 | { 101 | $this->dumpManagerAssets($this->am); 102 | $this->dumpManagerAssets($this->lam); 103 | } 104 | 105 | /** 106 | * Dumps the assets of given manager 107 | * 108 | * Doesn't use AssetWriter::writeManagerAssets since we also want to dump non-combined assets 109 | * (for example, when using twig extension in debug mode). 110 | * 111 | * @param AssetManager $am 112 | */ 113 | protected function dumpManagerAssets(AssetManager $am) 114 | { 115 | foreach ($am->getNames() as $name) { 116 | $asset = $am->get($name); 117 | 118 | if ($am instanceof LazyAssetManager) { 119 | $formula = $am->getFormula($name); 120 | } 121 | 122 | $this->writer->writeAsset($asset); 123 | 124 | if (!isset($formula[2])) { 125 | continue; 126 | } 127 | 128 | $debug = isset($formula[2]['debug']) ? $formula[2]['debug'] : $am->isDebug(); 129 | $combine = isset($formula[2]['combine']) ? $formula[2]['combine'] : null; 130 | 131 | if (null !== $combine ? !$combine : $debug) { 132 | foreach ($asset as $leaf) { 133 | $this->writer->writeAsset($leaf); 134 | } 135 | } 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/config/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/barryvdh/laravel-assetic/827f2223d4b0b314705a3b7387a2ff06546ba6f8/src/config/.gitkeep -------------------------------------------------------------------------------- /src/config/config.php: -------------------------------------------------------------------------------- 1 | true, 19 | 20 | 'options' => array( 21 | 22 | 'debug' => \Config::get('app.debug'), 23 | 24 | 'formulae_cache_dir' => storage_path().'/cache/assetic', 25 | 26 | 'auto_dump_assets' => \Config::get('app.debug') 27 | 28 | ), 29 | 30 | /* 31 | |-------------------------------------------------------------------------- 32 | | Filter Manager 33 | |-------------------------------------------------------------------------- 34 | | 35 | | A filter manager is also provided for organizing filters. 36 | | 37 | */ 38 | 'filter_manager' => function(FilterManager $fm){ 39 | // $fm->set('sass', new SassFilter('/path/to/parser/sass')); 40 | // $fm->set('yui_css', new Yui\CssCompressorFilter('/path/to/yuicompressor.jar')); 41 | }, 42 | 43 | /* 44 | |-------------------------------------------------------------------------- 45 | | Asset Manager 46 | |-------------------------------------------------------------------------- 47 | | 48 | | An asset manager is provided for organizing assets. 49 | | 50 | */ 51 | 'asset_manager' => function(AssetManager $am){ 52 | // $am->set('jquery', new FileAsset('/path/to/jquery.js')); 53 | // $am->set('base_css', new GlobAsset('/path/to/css/*')); 54 | }, 55 | 56 | 'path_to_web' => public_path(), 57 | 58 | //'path_to_source' => public_path(), // When path_to_source is not set, it is the same as path_to_web 59 | 60 | 61 | ); 62 | -------------------------------------------------------------------------------- /tests/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/barryvdh/laravel-assetic/827f2223d4b0b314705a3b7387a2ff06546ba6f8/tests/.gitkeep --------------------------------------------------------------------------------