Commit b317c082 by Qiang Xue

Merge pull request #309 from klimov-paul/asset-command

#72 Improve the "asset" command Iteration 2
parents 66a5b9c3 a0e54d62
...@@ -14,6 +14,8 @@ use yii\console\Controller; ...@@ -14,6 +14,8 @@ use yii\console\Controller;
/** /**
* This command allows you to combine and compress your JavaScript and CSS files. * This command allows you to combine and compress your JavaScript and CSS files.
* *
* @property array|\yii\web\AssetManager $assetManager asset manager, which will be used for assets processing.
*
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
...@@ -50,15 +52,15 @@ class AssetController extends Controller ...@@ -50,15 +52,15 @@ class AssetController extends Controller
*/ */
public $targets = array(); public $targets = array();
/** /**
* @var array configuration for [[yii\web\AssetManager]] instance, which will be used * @var array|\yii\web\AssetManager [[yii\web\AssetManager]] instance or its array configuration, which will be used
* for assets publishing. * for assets processing.
*/ */
public $assetManager = array(); private $_assetManager = array();
/** /**
* @var string|callback Java Script file compressor. * @var string|callback Java Script file compressor.
* If a string, it is treated as shell command template, which should contain * If a string, it is treated as shell command template, which should contain
* placeholders {from} - source file name - and {to} - output file name. * placeholders {from} - source file name - and {to} - output file name.
* If an array, it is treated as PHP callback, which should perform the compression. * Otherwise, it is treated as PHP callback, which should perform the compression.
* *
* Default value relies on usage of "Closure Compiler" * Default value relies on usage of "Closure Compiler"
* @see https://developers.google.com/closure/compiler/ * @see https://developers.google.com/closure/compiler/
...@@ -68,7 +70,7 @@ class AssetController extends Controller ...@@ -68,7 +70,7 @@ class AssetController extends Controller
* @var string|callback CSS file compressor. * @var string|callback CSS file compressor.
* If a string, it is treated as shell command template, which should contain * If a string, it is treated as shell command template, which should contain
* placeholders {from} - source file name - and {to} - output file name. * placeholders {from} - source file name - and {to} - output file name.
* If an array, it is treated as PHP callback, which should perform the compression. * Otherwise, it is treated as PHP callback, which should perform the compression.
* *
* Default value relies on usage of "YUI Compressor" * Default value relies on usage of "YUI Compressor"
* @see https://github.com/yui/yuicompressor/ * @see https://github.com/yui/yuicompressor/
...@@ -76,6 +78,42 @@ class AssetController extends Controller ...@@ -76,6 +78,42 @@ class AssetController extends Controller
public $cssCompressor = 'java -jar yuicompressor.jar {from} -o {to}'; public $cssCompressor = 'java -jar yuicompressor.jar {from} -o {to}';
/** /**
* Returns the asset manager instance.
* @throws \yii\console\Exception on invalid configuration.
* @return \yii\web\AssetManager asset manager instance.
*/
public function getAssetManager()
{
if (!is_object($this->_assetManager)) {
$options = $this->_assetManager;
if (!isset($options['class'])) {
$options['class'] = 'yii\\web\\AssetManager';
}
if (!isset($options['basePath'])) {
throw new Exception("Please specify 'basePath' for the 'assetManager' option.");
}
if (!isset($options['baseUrl'])) {
throw new Exception("Please specify 'baseUrl' for the 'assetManager' option.");
}
$this->_assetManager = Yii::createObject($options);
}
return $this->_assetManager;
}
/**
* Sets asset manager instance or configuration.
* @param \yii\web\AssetManager|array $assetManager asset manager instance or its array configuration.
* @throws \yii\console\Exception on invalid argument type.
*/
public function setAssetManager($assetManager)
{
if (is_scalar($assetManager)) {
throw new Exception('"' . get_class($this) . '::assetManager" should be either object or array - "' . gettype($assetManager) . '" given.');
}
$this->_assetManager = $assetManager;
}
/**
* Combines and compresses the asset files according to the given configuration. * Combines and compresses the asset files according to the given configuration.
* During the process new asset bundle configuration file will be created. * During the process new asset bundle configuration file will be created.
* You should replace your original asset bundle configuration with this file in order to use compressed files. * You should replace your original asset bundle configuration with this file in order to use compressed files.
...@@ -114,19 +152,14 @@ class AssetController extends Controller ...@@ -114,19 +152,14 @@ class AssetController extends Controller
echo "Loading configuration from '{$configFile}'...\n"; echo "Loading configuration from '{$configFile}'...\n";
foreach (require($configFile) as $name => $value) { foreach (require($configFile) as $name => $value) {
if (property_exists($this, $name)) { if (property_exists($this, $name) || $this->canSetProperty($name)) {
$this->$name = $value; $this->$name = $value;
} else { } else {
throw new Exception("Unknown configuration option: $name"); throw new Exception("Unknown configuration option: $name");
} }
} }
if (!isset($this->assetManager['basePath'])) { $this->getAssetManager(); // check asset manager configuration
throw new Exception("Please specify 'basePath' for the 'assetManager' option.");
}
if (!isset($this->assetManager['baseUrl'])) {
throw new Exception("Please specify 'baseUrl' for the 'assetManager' option.");
}
} }
/** /**
...@@ -138,27 +171,65 @@ class AssetController extends Controller ...@@ -138,27 +171,65 @@ class AssetController extends Controller
protected function loadBundles($bundles, $extensions) protected function loadBundles($bundles, $extensions)
{ {
echo "Collecting source bundles information...\n"; echo "Collecting source bundles information...\n";
$assetManager = $this->getAssetManager();
$result = array(); $result = array();
foreach ($bundles as $name => $bundle) {
$bundle['class'] = 'yii\\web\\AssetBundle'; $assetManager->bundles = $bundles;
$result[$name] = Yii::createObject($bundle); foreach ($assetManager->bundles as $name => $bundle) {
$result[$name] = $assetManager->getBundle($name);
} }
foreach ($extensions as $path) { foreach ($extensions as $path) {
$manifest = $path . '/assets.php'; $manifest = $path . '/assets.php';
if (!is_file($manifest)) { if (!is_file($manifest)) {
continue; continue;
} }
foreach (require($manifest) as $name => $bundle) { $assetManager->bundles = require($manifest);
foreach ($assetManager->bundles as $name => $bundle) {
if (!isset($result[$name])) { if (!isset($result[$name])) {
$bundle['class'] = 'yii\\web\\AssetBundle'; $result[$name] = $assetManager->getBundle($name);
$result[$name] = Yii::createObject($bundle);
} }
} }
} }
foreach ($result as $name => $bundle) {
$this->loadBundleDependency($name, $bundle, $result);
}
return $result; return $result;
} }
/** /**
* Loads asset bundle dependencies recursively.
* @param string $name bundle name
* @param \yii\web\AssetBundle $bundle bundle instance
* @param array $result already loaded bundles list.
* @throws \yii\console\Exception on failure.
*/
protected function loadBundleDependency($name, $bundle, &$result) {
if (!empty($bundle->depends)) {
$assetManager = $this->getAssetManager();
foreach ($bundle->depends as $dependencyName) {
if (!array_key_exists($dependencyName, $result)) {
$dependencyBundle = $assetManager->getBundle($dependencyName);
if ($dependencyBundle === null) {
throw new Exception("Unable to load dependency bundle '{$dependencyName}' for bundle '{$name}'.");
} else {
$result[$dependencyName] = false;
$this->loadBundleDependency($dependencyName, $dependencyBundle, $result);
$result[$dependencyName] = $dependencyBundle;
}
} else {
if ($result[$dependencyName] === false) {
throw new Exception("A circular dependency is detected for target '{$dependencyName}'.");
}
}
}
}
}
/**
* Creates full list of output asset bundles. * Creates full list of output asset bundles.
* @param array $targets output asset bundles configuration. * @param array $targets output asset bundles configuration.
* @param \yii\web\AssetBundle[] $bundles list of source asset bundles. * @param \yii\web\AssetBundle[] $bundles list of source asset bundles.
...@@ -222,17 +293,13 @@ class AssetController extends Controller ...@@ -222,17 +293,13 @@ class AssetController extends Controller
/** /**
* Publishes given asset bundles. * Publishes given asset bundles.
* @param \yii\web\AssetBundle[] $bundles asset bundles to be published. * @param \yii\web\AssetBundle[] $bundles asset bundles to be published.
* @param array $options assert manager instance configuration.
*/ */
protected function publishBundles($bundles, $options) protected function publishBundles($bundles)
{ {
echo "\nPublishing bundles:\n"; echo "\nPublishing bundles:\n";
if (!isset($options['class'])) { $assetManager = $this->getAssetManager();
$options['class'] = 'yii\\web\\AssetManager';
}
$am = Yii::createObject($options);
foreach ($bundles as $name => $bundle) { foreach ($bundles as $name => $bundle) {
$bundle->publish($am); $bundle->publish($assetManager);
echo " '".$name."' published.\n"; echo " '".$name."' published.\n";
} }
echo "\n"; echo "\n";
...@@ -259,7 +326,7 @@ class AssetController extends Controller ...@@ -259,7 +326,7 @@ class AssetController extends Controller
$inputFiles[] = $bundles[$name]->basePath . '/' . $file; $inputFiles[] = $bundles[$name]->basePath . '/' . $file;
} }
} else { } else {
throw new Exception("Unknown bundle: $name"); throw new Exception("Unknown bundle: '{$name}'");
} }
} }
if ($type === 'js') { if ($type === 'js') {
...@@ -339,6 +406,7 @@ class AssetController extends Controller ...@@ -339,6 +406,7 @@ class AssetController extends Controller
* Saves new asset bundles configuration. * Saves new asset bundles configuration.
* @param \yii\web\AssetBundle[] $targets list of asset bundles to be saved. * @param \yii\web\AssetBundle[] $targets list of asset bundles to be saved.
* @param string $bundleFile output file name. * @param string $bundleFile output file name.
* @throws \yii\console\Exception on failure.
*/ */
protected function saveTargets($targets, $bundleFile) protected function saveTargets($targets, $bundleFile)
{ {
......
...@@ -105,7 +105,7 @@ class AssetControllerTest extends TestCase ...@@ -105,7 +105,7 @@ class AssetControllerTest extends TestCase
), ),
'assetManager' => array( 'assetManager' => array(
'basePath' => $this->testAssetsBasePath, 'basePath' => $this->testAssetsBasePath,
'baseUrl' => $baseUrl, 'baseUrl' => '',
), ),
); );
return $config; return $config;
...@@ -207,6 +207,9 @@ class AssetControllerTest extends TestCase ...@@ -207,6 +207,9 @@ class AssetControllerTest extends TestCase
'app' => array( 'app' => array(
'css' => array_keys($cssFiles), 'css' => array_keys($cssFiles),
'js' => array_keys($jsFiles), 'js' => array_keys($jsFiles),
'depends' => array(
'yii',
),
), ),
);; );;
$bundleFile = $this->testFilePath . DIRECTORY_SEPARATOR . 'bundle.php'; $bundleFile = $this->testFilePath . DIRECTORY_SEPARATOR . 'bundle.php';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment