jsrink update 1.65

This commit is contained in:
gtbu 2023-03-26 15:18:49 +02:00
parent 8dcffe119b
commit bf8c93a53a
2 changed files with 132 additions and 30 deletions

View File

@ -74,6 +74,20 @@ class Minifier
*/
protected $c;
/**
* This character is only active when certain look ahead actions take place.
*
* @var string
*/
protected $last_char;
/**
* This character is only active when certain look ahead actions take place.
*
* @var string
*/
protected $output;
/**
* Contains the options for the current minification process.
*
@ -95,6 +109,9 @@ class Minifier
*/
protected static $defaultOptions = ['flaggedComments' => true];
protected static $keywords = ["delete", "do", "for", "in", "instanceof", "return", "typeof", "yield"];
/**
* Contains lock ids which are used to replace certain code patterns and
* prevent them from being minified
@ -115,17 +132,11 @@ class Minifier
public static function minify($js, $options = [])
{
try {
ob_start();
$jshrink = new Minifier();
$js = $jshrink->lock($js);
$jshrink->minifyDirectToOutput($js, $options);
// Sometimes there's a leading new line, so we trim that out here.
$js = ltrim(ob_get_clean());
$js = ltrim($jshrink->minifyToString($js, $options));
$js = $jshrink->unlock($js);
unset($jshrink);
return $js;
} catch (\Exception $e) {
if (isset($jshrink)) {
@ -134,9 +145,6 @@ class Minifier
$jshrink->clean();
unset($jshrink);
}
// without this call things get weird, with partially outputted js.
ob_end_clean();
throw $e;
}
}
@ -148,11 +156,12 @@ class Minifier
* @param string $js The raw javascript to be minified
* @param array $options Various runtime options in an associative array
*/
protected function minifyDirectToOutput($js, $options)
protected function minifyToString($js, $options)
{
$this->initialize($js, $options);
$this->loop();
$this->clean();
return $this->output;
}
/**
@ -164,7 +173,7 @@ class Minifier
protected function initialize($js, $options)
{
$this->options = array_merge(static::$defaultOptions, $options);
$this->input = str_replace(["\r\n", '/**/', "\r"], ["\n", "", "\n"], $js);
$this->input = $js;
// We add a newline to the end of the script to make it easier to deal
// with comments at the bottom of the script- this prevents the unclosed
@ -177,7 +186,9 @@ class Minifier
// Populate "a" with a new line, "b" with the first character, before
// entering the loop
$this->a = "\n";
$this->b = $this->getReal();
$this->b = "\n";
$this->last_char = "\n";
$this->output = "";
}
/**
@ -192,6 +203,13 @@ class Minifier
'[' => true,
'@' => true];
protected function echo($char) {
$this->output .= $char;
$this->last_char = $char[-1];
}
/**
* The primary action occurs here. This function loops through the input string,
* outputting anything that's relevant and discarding anything that is not.
@ -201,10 +219,11 @@ class Minifier
while ($this->a !== false && !is_null($this->a) && $this->a !== '') {
switch ($this->a) {
// new lines
case "\r":
case "\n":
// if the next line is something that can't stand alone preserve the newline
if ($this->b !== false && isset($this->noNewLineCharacters[$this->b])) {
echo $this->a;
$this->echo($this->a);
$this->saveString();
break;
}
@ -220,7 +239,7 @@ class Minifier
// no break
case ' ':
if (static::isAlphaNumeric($this->b)) {
echo $this->a;
$this->echo($this->a);
}
$this->saveString();
@ -228,14 +247,15 @@ class Minifier
default:
switch ($this->b) {
case "\r":
case "\n":
if (strpos('}])+-"\'', $this->a) !== false) {
echo $this->a;
$this->echo($this->a);
$this->saveString();
break;
} else {
if (static::isAlphaNumeric($this->a)) {
echo $this->a;
$this->echo($this->a);
$this->saveString();
}
}
@ -254,7 +274,7 @@ class Minifier
continue 3;
}
echo $this->a;
$this->echo($this->a);
$this->saveString();
break;
}
@ -263,9 +283,27 @@ class Minifier
// do reg check of doom
$this->b = $this->getReal();
if (($this->b == '/' && strpos('(,=:[!&|?', $this->a) !== false)) {
$this->saveRegex();
if ($this->b == '/') {
$valid_tokens = "(,=:[!&|?\n";
# Find last "real" token, excluding spaces.
$last_token = $this->a;
if ($last_token == " ") {
$last_token = $this->last_char;
}
if (strpos($valid_tokens, $last_token) !== false) {
// Regex can appear unquoted after these symbols
$this->saveRegex();
} else if ($this->endsInKeyword()) {
// This block checks for the "return" token before the slash.
$this->saveRegex();
}
}
// if (($this->b == '/' && strpos('(,=:[!&|?', $this->a) !== false)) {
// $this->saveRegex();
// }
}
}
@ -308,6 +346,12 @@ class Minifier
$this->index++;
}
# Convert all line endings to unix standard.
# `\r\n` converts to `\n\n` and is minified.
if ($char == "\r") {
$char = "\n";
}
// Normalize all whitespace except for the newline character into a
// standard space.
if ($char !== "\n" && $char < "\x20") {
@ -317,6 +361,36 @@ class Minifier
return $char;
}
/**
* This function returns the next character without moving the index forward.
*
*
* @return string The next character
* @throws \RuntimeException
*/
protected function peek()
{
if ($this->index >= $this->len) {
return false;
}
$char = $this->input[$this->index];
# Convert all line endings to unix standard.
# `\r\n` converts to `\n\n` and is minified.
if ($char == "\r") {
$char = "\n";
}
// Normalize all whitespace except for the newline character into a
// standard space.
if ($char !== "\n" && $char < "\x20") {
return ' ';
}
# Return the next character but don't push the index.
return $char;
}
/**
* This function gets the next "real" character. It is essentially a wrapper
* around the getChar function that skips comments. This has significant
@ -387,6 +461,15 @@ class Minifier
$this->getChar(); // current C
$thirdCommentString = $this->getChar();
// Detect a completely empty comment, ie `/**/`
if ($thirdCommentString == "*") {
$peekChar = $this->peek();
if ($peekChar == "/") {
$this->index++;
return;
}
}
// kill everything up to the next */ if it's there
if ($this->getNext('*/')) {
$this->getChar(); // get *
@ -400,17 +483,17 @@ class Minifier
// If conditional comments or flagged comments are not the first thing in the script
// we need to echo a and fill it with a space before moving on.
if ($startIndex > 0) {
echo $this->a;
$this->echo($this->a);
$this->a = " ";
// If the comment started on a new line we let it stay on the new line
if ($this->input[($startIndex - 1)] === "\n") {
echo "\n";
$this->echo("\n");
}
}
$endPoint = ($this->index - 1) - $startIndex;
echo substr($this->input, $startIndex, $endPoint);
$this->echo(substr($this->input, $startIndex, $endPoint));
$this->c = $char;
@ -476,7 +559,7 @@ class Minifier
$stringType = $this->a;
// Echo out that starting quote
echo $this->a;
$this->echo($this->a);
// Loop until the string is done
// Grab the very next character and load it into a
@ -495,7 +578,7 @@ class Minifier
// block below.
case "\n":
if ($stringType === '`') {
echo $this->a;
$this->echo($this->a);
} else {
throw new \RuntimeException('Unclosed string at position: ' . $startpos);
}
@ -515,14 +598,14 @@ class Minifier
}
// echo out the escaped character and restart the loop.
echo $this->a . $this->b;
$this->echo($this->a . $this->b);
break;
// Since we're not dealing with any special cases we simply
// output the character and continue our loop.
default:
echo $this->a;
$this->echo($this->a);
}
}
}
@ -535,7 +618,11 @@ class Minifier
*/
protected function saveRegex()
{
echo $this->a . $this->b;
if ($this->a != " ") {
$this->echo($this->a);
}
$this->echo($this->b);
while (($this->a = $this->getChar()) !== false) {
if ($this->a === '/') {
@ -543,7 +630,7 @@ class Minifier
}
if ($this->a === '\\') {
echo $this->a;
$this->echo($this->a);
$this->a = $this->getChar();
}
@ -551,7 +638,7 @@ class Minifier
throw new \RuntimeException('Unclosed regex pattern at position: ' . $this->index);
}
echo $this->a;
$this->echo($this->a);
}
$this->b = $this->getReal();
}
@ -567,6 +654,21 @@ class Minifier
return preg_match('/^[\w\$\pL]$/', $char) === 1 || $char == '/';
}
protected function endsInKeyword() {
# When this function is called A is not yet assigned to output.
$testOutput = $this->output . $this->a;
foreach(static::$keywords as $keyword) {
if (preg_match('/[^\w]'.$keyword.'[ ]?$/i', $testOutput) === 1) {
return true;
}
}
return false;
}
/**
* Replace patterns in the given string and store the replacement
*