mirror of
https://github.com/mrclay/minify.git
synced 2025-08-07 14:46:29 +02:00
Closure Compiler API minifier and unit test
JSMin preserves "/*!"
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
Minify Release History
|
||||
|
||||
Version 2.1.4
|
||||
* Option to minify JS with Closure Compiler API w/ JSMin failover
|
||||
* Cookie/bookmarklet-based debug mode. No HTML editing!
|
||||
* Allows 1 file to be missing w/o complete failure
|
||||
* Combine multiple groups and files in single URI
|
||||
|
@@ -102,6 +102,18 @@ $min_serveOptions['bubbleCssImports'] = false;
|
||||
$min_serveOptions['maxAge'] = 1800;
|
||||
|
||||
|
||||
/**
|
||||
* To use Google's Closure Compiler API (falling back to JSMin on failure),
|
||||
* uncomment the following lines:
|
||||
*/
|
||||
/*function closureCompiler($js) {
|
||||
require_once 'Minify/JS/ClosureCompiler.php';
|
||||
return Minify_JS_ClosureCompiler::minify($js);
|
||||
}
|
||||
$min_serveOptions['minifiers']['application/x-javascript'] = 'closureCompiler';
|
||||
//*/
|
||||
|
||||
|
||||
/**
|
||||
* If you'd like to restrict the "f" option to files within/below
|
||||
* particular directories below DOCUMENT_ROOT, set this here.
|
||||
|
@@ -308,7 +308,7 @@ class JSMin {
|
||||
$this->get();
|
||||
// if comment preserved by YUI Compressor
|
||||
if (0 === strpos($comment, '!')) {
|
||||
return "\n/*" . substr($comment, 1) . "*/\n";
|
||||
return "\n/*!" . substr($comment, 1) . "*/\n";
|
||||
}
|
||||
// if IE conditional comment
|
||||
if (preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
|
||||
|
109
min/lib/Minify/JS/ClosureCompiler.php
Normal file
109
min/lib/Minify/JS/ClosureCompiler.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
/**
|
||||
* Class Minify_JS_ClosureCompiler
|
||||
* @package Minify
|
||||
*/
|
||||
|
||||
/**
|
||||
* Minify Javascript using Google's Closure Compiler API
|
||||
*
|
||||
* @link http://code.google.com/closure/compiler/
|
||||
* @package Minify
|
||||
* @author Stephen Clay <steve@mrclay.org>
|
||||
*/
|
||||
class Minify_JS_ClosureCompiler {
|
||||
const URL = 'http://closure-compiler.appspot.com/compile';
|
||||
|
||||
/**
|
||||
* Minify Javascript code via HTTP request to the Closure Compiler API
|
||||
*
|
||||
* @param string $js input code
|
||||
* @param array $options unused at this point
|
||||
* @return string
|
||||
*/
|
||||
public static function minify($js, array $options = array())
|
||||
{
|
||||
$obj = new self($options);
|
||||
return $obj->min($js);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
* fallbackFunc : default array($this, 'fallback');
|
||||
*/
|
||||
public function __construct(array $options = array())
|
||||
{
|
||||
$this->_fallbackFunc = isset($options['fallbackMinifier'])
|
||||
? $options['fallbackMinifier']
|
||||
: array($this, '_fallback');
|
||||
}
|
||||
|
||||
public function min($js)
|
||||
{
|
||||
$content = $this->_getPostContent($js);
|
||||
$bytes = (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2))
|
||||
? mb_strlen($content, '8bit')
|
||||
: strlen($content);
|
||||
if ($bytes > 200000) {
|
||||
throw new Minify_JS_ClosureCompiler_Exception(
|
||||
'POST content larger than 200000 bytes'
|
||||
);
|
||||
}
|
||||
$response = $this->_getResponse($content);
|
||||
if (preg_match('/^Error\(\d\d?\):/', $response)) {
|
||||
if (is_callable($this->_fallbackFunc)) {
|
||||
$response = "/* Received errors from Closure Compiler API:\n$response"
|
||||
. "\n(Using fallback minifier)\n*/\n";
|
||||
$response .= call_user_func($this->_fallbackFunc, $js);
|
||||
} else {
|
||||
throw new Minify_JS_ClosureCompiler_Exception($response);
|
||||
}
|
||||
}
|
||||
if ($response === '') {
|
||||
$errors = $this->_getResponse($this->_getPostContent($js, true));
|
||||
throw new Minify_JS_ClosureCompiler_Exception($errors);
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
protected $_fallbackFunc = null;
|
||||
|
||||
protected function _getResponse($content)
|
||||
{
|
||||
$contents = file_get_contents(self::URL, false, stream_context_create(array(
|
||||
'http' => array(
|
||||
'method' => 'POST',
|
||||
'header' => 'Content-type: application/x-www-form-urlencoded',
|
||||
'content' => $content,
|
||||
'max_redirects' => 0,
|
||||
'timeout' => 15,
|
||||
)
|
||||
)));
|
||||
if (false === $contents) {
|
||||
throw new Minify_JS_ClosureCompiler_Exception(
|
||||
"No HTTP response from server"
|
||||
);
|
||||
}
|
||||
return trim($contents);
|
||||
}
|
||||
|
||||
protected function _getPostContent($js, $returnErrors = false)
|
||||
{
|
||||
return http_build_query(array(
|
||||
'js_code' => $js,
|
||||
'output_info' => ($returnErrors ? 'errors' : 'compiled_code'),
|
||||
'output_format' => 'text',
|
||||
'compilation_level' => 'SIMPLE_OPTIMIZATIONS'
|
||||
));
|
||||
}
|
||||
|
||||
protected function _fallback($js)
|
||||
{
|
||||
require_once 'JSMin.php';
|
||||
return JSMin::minify($js);
|
||||
}
|
||||
}
|
||||
|
||||
class Minify_JS_ClosureCompiler_Exception extends Exception {}
|
4
min_unit_tests/_test_files/js/before.min.js
vendored
4
min_unit_tests/_test_files/js/before.min.js
vendored
@@ -1,10 +1,10 @@
|
||||
/* is.js
|
||||
/*! is.js
|
||||
|
||||
(c) 2001 Douglas Crockford
|
||||
2001 June 3
|
||||
*/
|
||||
var is={ie:navigator.appName=='Microsoft Internet Explorer',java:navigator.javaEnabled(),ns:navigator.appName=='Netscape',ua:navigator.userAgent.toLowerCase(),version:parseFloat(navigator.appVersion.substr(21))||parseFloat(navigator.appVersion),win:navigator.platform=='Win32'}
|
||||
/**
|
||||
/*!*
|
||||
* preserve this comment, too
|
||||
*/
|
||||
is.mac=is.ua.indexOf('mac')>=0;if(is.ua.indexOf('opera')>=0){is.ie=is.ns=false;is.opera=true;}
|
||||
|
49
min_unit_tests/test_Minify_JS_ClosureCompiler.php
Normal file
49
min_unit_tests/test_Minify_JS_ClosureCompiler.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
require_once '_inc.php';
|
||||
|
||||
require_once 'Minify/JS/ClosureCompiler.php';
|
||||
|
||||
function test_Minify_JS_ClosureCompiler()
|
||||
{
|
||||
global $thisDir;
|
||||
|
||||
$src = "
|
||||
(function (window, undefined){
|
||||
function addOne(input) {
|
||||
return 1 + input;
|
||||
}
|
||||
window.addOne = addOne;
|
||||
window.undefined = undefined;
|
||||
})(window);
|
||||
";
|
||||
$minExpected = "(function(a,b){a.addOne=function(c){return 1+c};a.undefined=b})(window);";
|
||||
$minOutput = Minify_JS_ClosureCompiler::minify($src);
|
||||
if (false !== strpos($minOutput, 'Error(22): Too many compiles')) {
|
||||
echo "!NOTE: Too many recent calls to Closure Compiler API to test.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$passed = assertTrue($minExpected == $minOutput, 'Minify_JS_ClosureCompiler : Overall');
|
||||
if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) {
|
||||
echo "\n---Output: " .countBytes($minOutput). " bytes\n\n{$minOutput}\n\n";
|
||||
echo "---Expected: " .countBytes($minExpected). " bytes\n\n{$minExpected}\n\n";
|
||||
echo "---Source: " .countBytes($src). " bytes\n\n{$src}\n\n\n";
|
||||
}
|
||||
|
||||
$src = "function blah({ return 'blah';} ";
|
||||
$exc = null;
|
||||
try {
|
||||
$minOutput = Minify_JS_ClosureCompiler::minify($src);
|
||||
} catch (Exception $e) {
|
||||
$exc = $e;
|
||||
}
|
||||
$passed = assertTrue(
|
||||
$exc instanceof Minify_JS_ClosureCompiler_Exception
|
||||
, 'Minify_JS_ClosureCompiler : Throws Minify_JS_ClosureCompiler_Exception');
|
||||
if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) {
|
||||
echo "\n---Message: " . var_export($exc->getMessage(), 1) . "\n\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
test_Minify_JS_ClosureCompiler();
|
@@ -9,6 +9,7 @@ require 'test_Minify_Cache_Memcache.php';
|
||||
require 'test_Minify_Cache_ZendPlatform.php';
|
||||
require 'test_Minify_CSS.php';
|
||||
require 'test_Minify_CSS_UriRewriter.php';
|
||||
require 'test_Minify_JS_ClosureCompiler.php';
|
||||
require 'test_Minify_CommentPreserver.php';
|
||||
require 'test_Minify_HTML.php';
|
||||
require 'test_Minify_ImportProcessor.php';
|
||||
|
Reference in New Issue
Block a user