Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ClosureCompiler filter. #170

Merged
merged 3 commits into from Mar 22, 2014
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
133 changes: 133 additions & 0 deletions Lib/Filter/ClosureCompiler.php
@@ -0,0 +1,133 @@
<?php

App::uses('AssetFilter', 'AssetCompress.Lib');

/**
* Google Closure Compiler API Filter
*
* Allows you to filter Javascript files through the Google Closure compiler API. The script
* needs to have web access to run.
*
* @package AssetCompress.Lib.Filter
*/
class ClosureCompiler extends AssetFilter {

/**
* Defaults.
*
* @var array
*/
protected $_defaults = array('compilation_level' => 'WHITESPACE_ONLY');

/**
* Settings.
*
* NOTE: statistics and warnings are only used when in debug mode.
*
* - level (string) Defaults to WHITESPACE_ONLY. Values: SIMPLE_OPTIMIZATIONS, ADVANCED_OPTIMIZATIONS.
* - print (string) How to output the errors, statistics and/or warnings.
* - statistics (boolean) Defaults to FALSE.
* - warnings (mixed) Defaults to FALSE. Values: TRUE or QUIET, DEFAULT, VERBOSE.
*
* @var array
*/
protected $_settings = array(
'level' => null,
'print' => "%s:\n%s\n",
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be configurable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No.

'statistics' => false,
'warnings' => false
);

/**
* Optional API parameters.
*
* - The `output_file_name` hasn't been included because AssetCompress is used for saving the minified javascript.
* - The `warning_level` is automatically handled in `self::$_settings`.
*
* @var array
* @see https://developers.google.com/closure/compiler/docs/api-ref
*/
private $__params = array(
'js_externs',
'externs_url',
'exclude_default_externs',
'formatting',
'use_closure_library',
'language'
);

/**
* {@inheritdoc}
*/
public function output($filename, $content) {
$defaults = array('compilation_level' => $this->_settings['level']);

$errors = $this->_query($content, array('output_info' => 'errors'));
if (!empty($errors)) {
printf($this->_settings['print'], 'Errors', $errors);
exit;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is probably better to throw an exception instead of calling exit here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed.

}

$output = $this->_query($content, array('output_info' => 'compiled_code'));

if (!Configure::read('debug')) {
return $output;
}

foreach ($this->_settings as $setting => $value) {
if (!in_array($setting, array('warnings', 'statistics')) || true != $value) {
continue;
}

$args = array('output_info' => $setting);
if ('warnings' == $setting && in_array($value, array('QUIET', 'DEFAULT', 'VERBOSE'))) {
$args['warning_level'] = $value;
}

$$setting = $this->_query($content, $args);
printf($this->_settings['print'], ucfirst($setting), $$setting);
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this printing here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the shell command's output. Should I remove it?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not overly familiar with the closure webservice, what purpose does printing this out serve?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Closure can return an output:

output_info
The value of this parameter indicates the kind of information that you want from the compiler.
There are four possible kinds of output: compiled_code, warnings, errors, and statistics. This
example uses the value compiled_code, which tells the Closure Compiler service to output the
compressed version of the JavaScript it receives in the request.

In here it's used to output 'warnings' and/or 'statistics'.


return $output;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it helpful to have the warnings dumped?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe so, depending on the compression level, different warnings are thrown.

}

/**
* Query the Closure compiler API.
*
* @param string $content Javascript to compile.
* @param array $args API parameters.
* @throws Exception If curl triggers an error.
* @return string
*/
protected function _query($content, $args = array()) {
$args = array_merge($this->_defaults, $args);
if (!empty($this->_settings['level'])) {
$args['compilation_level'] = $this->_settings['level'];
}

foreach ($this->_settings as $key => $val) {
if (in_array($key, $this->__params)) {
$args[$key] = $val;
}
}

$ch = curl_init();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably should throw an exception when the environment is lacking curl.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using HttpSocket would be better as some environments may lack curl support.

curl_setopt_array($ch, array(
CURLOPT_URL => 'http://closure-compiler.appspot.com/compile',
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => 'js_code=' . urlencode($content) . '&' . http_build_query($args),
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_HEADER => 0,
CURLOPT_FOLLOWLOCATION => 0
));

$output = curl_exec($ch);

if (false === $output) {
throw new Exception('Curl error: ' . curl_error($ch));
}

curl_close($ch);
return $output;
}
}