Asset And Script Manager
Posted 18 March 2013 - 08:10 AM
Posted 31 March 2013 - 11:27 AM
My client script packages configuration now looks like this:
return array( 'yiicore' => array( 'depends' => array( //"standard" packages 'yii', 'yiiactiveform', ... ), ), 'zii.js' => array( 'basePath' => 'zii.widgets.assets', 'js' => array( 'gridview/jquery.yiigridview.js', 'listview/jquery.yiilistview.js', ), ), 'myExt' => array( 'basePath' => 'common.extensions.go.assets', 'js' => array( 'jquery.blockUI.js', 'json2.js', ... ), 'depends' => array( 'jquery', 'yiicore', 'jquery.ui', 'zii.js', ), ), 'site' => array( 'baseUrl' => '', 'js' => array( 'js/easyTooltip.js', 'js/ie-hover.js', ), 'css' => array( 'css/custom-theme/jquery-ui-1.8.4.custom.css', 'css/yii/gridview/styles.css', ... ), 'depends' => array( 'myExt', ), ), );
Package registration looks like this:
Client script config:
'clientScript'=>array( 'class' => 'app.components.AppClientScript', 'packages'=> require(dirname(__FILE__).'/components/clientScript.php'), 'minifyCss' => true, //keep it enabled, used in testprod configs 'minifyJs' => true, //keep it enabled, used in testprod configs 'useGoogleScripts' => false ),
1. No "random" registerScriptFile() calls - use packages only (extensions still use registerScriptFile() calls, but this is ignored)
2. Package can have "baseUrl" (url on the server, for js/css from public folder) or "basePath" (assets inside the protected folder, should be published first)
3. Package can contain both js and css (better not to do this, see below)
4. Only one package can be registered (see explanation below)
5. Scripts like jquery can be included from the google CDN ('useGoogleScripts' setting in the config)
6. Depending on the config settings we can keep full js/css or automatically minify them (I use modified minscript (http://www.yiiframew...sion/minscript/) extension for this)
7. There are dependencies and packages can include other packages.
Bonus feature (not related to packages) - special implementation of beginScript / endScript, so the js code in a view can be added like this:
<?php Yii::app()->clientScript->beginScript('billing-subscribe'); ?>
data: "some data"
What can be improved/changed in my implementation:
* Point (3) - "package can have both js and css" - this is probably not good and it is better to have separate packages with js/ts/css/less. We can specify 'type' for each package and based on the type we can know how to process this package (compress and join or compile first and then join, etc).
* Point (4) - only one package can be registered - this limitation was initially implemented by intention. When scripts are minified then the page contains only one combined js and one css. But now I think it is better to remove it and allow to register additional packages, so the page can contain more then one combined js or css (this is useful in some cases).
* Point (6) - now packages can only be minified on-the-fly and it is better to have also a console command to generate minified packages off-line.
In general I do not think that my current implementation can be reused (here is my client script class), because it rely on the minscript extension.
But I think that following ideas can be taken into consideration:
1) Separate package handling logic (packages description and registration) from the build process.
Now I see 'process' keys in the packages configuration and it would be better to remove it.
Instead we can have some 'type' or 'processor' key which will point to the builder object class. So we can have different builders like JSBuilder (compress and glue js), CSSBuilder (compress and glue css), LESSBuilder (complie less and then compress and glue), etc. Builders can then be used to do on-the-fly or offline package compilation.
2) How to deal with gluing of js / css files. I think the most simple way is to build each package independently, taking into account dependencies. Then we can include into the web page one meta-package which depends on all other packages or include individual packages.
3) Allow to easily switch between compressed and non-compressed scripts
4) Allow to use scripts both from public folder (no need to publish then to assets) and from the protected folder (publish to assets)
5) Consider adding a support for scripts inclusion from google CDN. It would be good to have an option to easily switch from local versions to CDN.
Posted 01 April 2013 - 04:03 AM
2. There's a map setting for it.
Posted 01 April 2013 - 05:59 AM
Then we just specify different processors for these packages.
Basically the idea is to specify processor class or class+params instead of callback.
This also means that we need to have files of the same type for one package, so the structure will be:
return array( 'packageX' => array( 'basePath' => '@app/scripts' 'processor' => 'GoogleClosureCompilerProcessor' // or 'processor' => array( 'class' => 'GoogleClosureCompilerProcessor', 'param1' => 'xxx' ), ), 'packageY' => array( 'basePath' => '@app/scripts' 'processor' => 'DirectPublishProcessor' ), );
With map setting as it described now it can be confusing, for example:
'package1' => array( 'basePath' => '@app/scripts' 'map' => array( 'one.js' => 'common.js', 'two.js' => 'common.js', '*.js' => 'all.js', ), 'process' => array( // do we need to duplicate map structure from the above? 'one.js' => function($fileName, $outputDir) ... 'two.js' => function($fileName, $outputDir) ... '*.js' => function($fileName, $outputDir) ... ), ),
Here if we can specify '*.js' then it should be also possible to specify individual scripts like 'one.js', yes?
Also will '*.js' be recursive (if there are subfolders below the app/scripts)?
So I think it is better to have packages for files of the same type. By default package will include all appropriate files from the 'basePath'.
We can also allow to specify individual files to include or constructs like '*' and '**' to include all files / recursively include all files. But this is optional and can be postponed - the developer can create appropriate folders structure, so one package = one folder + other packages as dependencies (or just dependencies).
And the 'map' parameter should go to processor configuration:
return array( 'packageJsX' => array( 'basePath' => '@app/scripts' // all scripts from the base path except these: 'exclude' => array( 'subfolder/script1.js', 'subfolder/script2.js', ), 'processor' => array( 'class' => 'GoogleClosureCompilerProcessor', 'map' => 'all.js', // add complied files to 'all.js' ), ), 'packageJsY' => array( 'basePath' => '@app/scripts' // include two scripts from the base path: 'include' => array( 'subfolder/script1.js', 'subfolder/script2.js', ), 'processor' => 'DirectPublishJSProcessor' ), 'packageJsAll' => array( 'depends' => array( 'packageJsX', 'packageJsY', ), 'processor' => false //or not specified ), );
Posted 01 April 2013 - 08:20 AM
Posted 01 April 2013 - 09:15 AM
Actually yes, this is about what at the github. Sorry, I didn't read the full discussion and thought that description at the top is up-to-date.
It is not completely clear how processors will be implemented - will we have something like ComplieLessToCssAndCompressProcessor or will be able to apply two processors (CompileLessProcessor and CompressCssProcessor)?