MDL-75465 lib: Upgrade minify to 1.3.69
This commit is contained in:
@@ -44,6 +44,10 @@ class CSS extends Minify
|
||||
'jpeg' => 'data:image/jpeg',
|
||||
'svg' => 'data:image/svg+xml',
|
||||
'woff' => 'data:application/x-font-woff',
|
||||
'woff2' => 'data:application/x-font-woff2',
|
||||
'avif' => 'data:image/avif',
|
||||
'apng' => 'data:image/apng',
|
||||
'webp' => 'data:image/webp',
|
||||
'tif' => 'image/tiff',
|
||||
'tiff' => 'image/tiff',
|
||||
'xbm' => 'image/x-xbitmap',
|
||||
@@ -637,35 +641,7 @@ class CSS extends Minify
|
||||
|
||||
return $placeholder;
|
||||
};
|
||||
// Moodle-specific change MDL-68191 starts.
|
||||
/* This was the old code:
|
||||
$this->registerPattern('/\n?\/\*(!|.*?@license|.*?@preserve).*?\*\/\n?/s', $callback);
|
||||
*/
|
||||
// This is the new, more accurate and faster regex.
|
||||
$this->registerPattern('/
|
||||
# optional newline
|
||||
\n?
|
||||
|
||||
# start comment
|
||||
\/\*
|
||||
|
||||
# comment content
|
||||
(?:
|
||||
# either starts with an !
|
||||
!
|
||||
|
|
||||
# or, after some number of characters which do not end the comment
|
||||
(?:(?!\*\/).)*?
|
||||
|
||||
# there is either a @license or @preserve tag
|
||||
@(?:license|preserve)
|
||||
)
|
||||
|
||||
# then match to the end of the comment
|
||||
.*?\*\/\n?
|
||||
|
||||
/ixs', $callback);
|
||||
// Moodle-specific change MDL-68191.
|
||||
|
||||
$this->registerPattern('/\/\*.*?\*\//s', '');
|
||||
}
|
||||
@@ -746,7 +722,7 @@ class CSS extends Minify
|
||||
// and since we've captured more code than required, we may have some leftover
|
||||
// calc() in here too - go recursive on the remaining but of code to go figure
|
||||
// that out and extract what is needed
|
||||
$rest = str_replace($function.$expr, '', $match[0]);
|
||||
$rest = $minifier->str_replace_first($function.$expr, '', $match[0]);
|
||||
$rest = preg_replace_callback($pattern, $callback, $rest);
|
||||
|
||||
return $placeholder.$rest;
|
||||
@@ -764,7 +740,8 @@ class CSS extends Minify
|
||||
// PHP only supports $this inside anonymous functions since 5.4
|
||||
$minifier = $this;
|
||||
$this->registerPattern(
|
||||
'/(?<=^|[;}])(--[^:;{}"\'\s]+)\s*:([^;{}]+)/m',
|
||||
|
||||
'/(?<=^|[;}{])\s*(--[^:;{}"\'\s]+)\s*:([^;{}]+)/m',
|
||||
function ($match) use ($minifier) {
|
||||
$placeholder = '--custom-'. count($minifier->extracted) . ':0';
|
||||
$minifier->extracted[$placeholder] = $match[1] .':'. trim($match[2]);
|
||||
|
||||
@@ -198,15 +198,25 @@ class JS extends Minify
|
||||
// PHP only supports $this inside anonymous functions since 5.4
|
||||
$minifier = $this;
|
||||
$callback = function ($match) use ($minifier) {
|
||||
$count = count($minifier->extracted);
|
||||
$placeholder = '/*'.$count.'*/';
|
||||
$minifier->extracted[$placeholder] = $match[0];
|
||||
if (
|
||||
substr($match[2], 0, 1) === '!' ||
|
||||
strpos($match[2], '@license') !== false ||
|
||||
strpos($match[2], '@preserve') !== false
|
||||
) {
|
||||
// preserve multi-line comments that start with /*!
|
||||
// or contain @license or @preserve annotations
|
||||
$count = count($minifier->extracted);
|
||||
$placeholder = '/*'.$count.'*/';
|
||||
$minifier->extracted[$placeholder] = $match[0];
|
||||
|
||||
return $placeholder;
|
||||
return $match[1] . $placeholder . $match[3];
|
||||
}
|
||||
|
||||
return $match[1] . $match[3];
|
||||
};
|
||||
|
||||
// multi-line comments
|
||||
$this->registerPattern('/\n?\/\*(!|.*?@license|.*?@preserve).*?\*\/\n?/s', $callback);
|
||||
$this->registerPattern('/\/\*.*?\*\//s', '');
|
||||
$this->registerPattern('/(\n?)\/\*(.*?)\*\/(\n?)/s', $callback);
|
||||
|
||||
// single-line comments
|
||||
$this->registerPattern('/\/\/.*$/m', '');
|
||||
@@ -254,7 +264,7 @@ class JS extends Minify
|
||||
// of the RegExp methods (a `\` followed by a variable or value is
|
||||
// likely part of a division, not a regex)
|
||||
$keywords = array('do', 'in', 'new', 'else', 'throw', 'yield', 'delete', 'return', 'typeof');
|
||||
$before = '(^|[=:,;\+\-\*\/\}\(\{\[&\|!]|'.implode('|', $keywords).')\s*';
|
||||
$before = '(^|[=:,;\+\-\*\?\/\}\(\{\[&\|!]|'.implode('|', $keywords).')\s*';
|
||||
$propertiesAndMethods = array(
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Properties_2
|
||||
'constructor',
|
||||
@@ -274,7 +284,7 @@ class JS extends Minify
|
||||
);
|
||||
$delimiters = array_fill(0, count($propertiesAndMethods), '/');
|
||||
$propertiesAndMethods = array_map('preg_quote', $propertiesAndMethods, $delimiters);
|
||||
$after = '(?=\s*([\.,;\)\}&\|+]|\/\/|$|\.('.implode('|', $propertiesAndMethods).')))';
|
||||
$after = '(?=\s*([\.,;:\)\}&\|+]|\/\/|$|\.('.implode('|', $propertiesAndMethods).')))';
|
||||
$this->registerPattern('/'.$before.'\K'.$pattern.$after.'/', $callback);
|
||||
|
||||
// regular expressions following a `)` are rather annoying to detect...
|
||||
@@ -405,9 +415,26 @@ class JS extends Minify
|
||||
* to be the for-loop's body... Same goes for while loops.
|
||||
* I'm going to double that semicolon (if any) so after the next line,
|
||||
* which strips semicolons here & there, we're still left with this one.
|
||||
* Note the special recursive construct in the three inner parts of the for:
|
||||
* (\{([^\{\}]*(?-2))*[^\{\}]*\})? - it is intended to match inline
|
||||
* functions bodies, e.g.: i<arr.map(function(e){return e}).length.
|
||||
* Also note that the construct is applied only once and multiplied
|
||||
* for each part of the for, otherwise it risks a catastrophic backtracking.
|
||||
* The limitation is that it will not allow closures in more than one
|
||||
* of the three parts for a specific for() case.
|
||||
* REGEX throwing catastrophic backtracking: $content = preg_replace('/(for\([^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*;[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*;[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*\));(\}|$)/s', '\\1;;\\8', $content);
|
||||
*/
|
||||
$content = preg_replace('/(for\([^;\{]*;[^;\{]*;[^;\{]*\));(\}|$)/s', '\\1;;\\2', $content);
|
||||
$content = preg_replace('/(for\((?:[^;\{]*|[^;\{]*function[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*);[^;\{]*;[^;\{]*\));(\}|$)/s', '\\1;;\\4', $content);
|
||||
$content = preg_replace('/(for\([^;\{]*;(?:[^;\{]*|[^;\{]*function[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*);[^;\{]*\));(\}|$)/s', '\\1;;\\4', $content);
|
||||
$content = preg_replace('/(for\([^;\{]*;[^;\{]*;(?:[^;\{]*|[^;\{]*function[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*)\));(\}|$)/s', '\\1;;\\4', $content);
|
||||
|
||||
$content = preg_replace('/(for\([^;\{]+\s+in\s+[^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
|
||||
|
||||
/*
|
||||
* Do the same for the if's that don't have a body but are followed by ;}
|
||||
*/
|
||||
$content = preg_replace('/(\bif\s*\([^{;]*\));\}/s', '\\1;;}', $content);
|
||||
|
||||
/*
|
||||
* Below will also keep `;` after a `do{}while();` along with `while();`
|
||||
* While these could be stripped after do-while, detecting this
|
||||
|
||||
@@ -243,6 +243,9 @@ abstract class Minify
|
||||
/**
|
||||
* Register a pattern to execute against the source content.
|
||||
*
|
||||
* If $replacement is a string, it must be plain text. Placeholders like $1 or \2 don't work.
|
||||
* If you need that functionality, use a callback instead.
|
||||
*
|
||||
* @param string $pattern PCRE pattern
|
||||
* @param string|callable $replacement Replacement value for matched pattern
|
||||
*/
|
||||
@@ -268,11 +271,13 @@ abstract class Minify
|
||||
*/
|
||||
protected function replace($content)
|
||||
{
|
||||
$processed = '';
|
||||
$contentLength = strlen($content);
|
||||
$output = '';
|
||||
$processedOffset = 0;
|
||||
$positions = array_fill(0, count($this->patterns), -1);
|
||||
$matches = array();
|
||||
|
||||
while ($content) {
|
||||
while ($processedOffset < $contentLength) {
|
||||
// find first match for all patterns
|
||||
foreach ($this->patterns as $i => $pattern) {
|
||||
list($pattern, $replacement) = $pattern;
|
||||
@@ -285,12 +290,12 @@ abstract class Minify
|
||||
|
||||
// no need to re-run matches that are still in the part of the
|
||||
// content that hasn't been processed
|
||||
if ($positions[$i] >= 0) {
|
||||
if ($positions[$i] >= $processedOffset) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$match = null;
|
||||
if (preg_match($pattern, $content, $match, PREG_OFFSET_CAPTURE)) {
|
||||
if (preg_match($pattern, $content, $match, PREG_OFFSET_CAPTURE, $processedOffset)) {
|
||||
$matches[$i] = $match;
|
||||
|
||||
// we'll store the match position as well; that way, we
|
||||
@@ -307,61 +312,52 @@ abstract class Minify
|
||||
|
||||
// no more matches to find: everything's been processed, break out
|
||||
if (!$matches) {
|
||||
$processed .= $content;
|
||||
// output the remaining content
|
||||
$output .= substr($content, $processedOffset);
|
||||
break;
|
||||
}
|
||||
|
||||
// see which of the patterns actually found the first thing (we'll
|
||||
// only want to execute that one, since we're unsure if what the
|
||||
// other found was not inside what the first found)
|
||||
$discardLength = min($positions);
|
||||
$firstPattern = array_search($discardLength, $positions);
|
||||
$match = $matches[$firstPattern][0][0];
|
||||
$matchOffset = min($positions);
|
||||
$firstPattern = array_search($matchOffset, $positions);
|
||||
$match = $matches[$firstPattern];
|
||||
|
||||
// execute the pattern that matches earliest in the content string
|
||||
list($pattern, $replacement) = $this->patterns[$firstPattern];
|
||||
$replacement = $this->replacePattern($pattern, $replacement, $content);
|
||||
list(, $replacement) = $this->patterns[$firstPattern];
|
||||
|
||||
// figure out which part of the string was unmatched; that's the
|
||||
// part we'll execute the patterns on again next
|
||||
$content = (string) substr($content, $discardLength);
|
||||
$unmatched = (string) substr($content, strpos($content, $match) + strlen($match));
|
||||
|
||||
// move the replaced part to $processed and prepare $content to
|
||||
// again match batch of patterns against
|
||||
$processed .= substr($replacement, 0, strlen($replacement) - strlen($unmatched));
|
||||
$content = $unmatched;
|
||||
|
||||
// first match has been replaced & that content is to be left alone,
|
||||
// the next matches will start after this replacement, so we should
|
||||
// fix their offsets
|
||||
foreach ($positions as $i => $position) {
|
||||
$positions[$i] -= $discardLength + strlen($match);
|
||||
}
|
||||
// add the part of the input between $processedOffset and the first match;
|
||||
// that content wasn't matched by anything
|
||||
$output .= substr($content, $processedOffset, $matchOffset - $processedOffset);
|
||||
// add the replacement for the match
|
||||
$output .= $this->executeReplacement($replacement, $match);
|
||||
// advance $processedOffset past the match
|
||||
$processedOffset = $matchOffset + strlen($match[0][0]);
|
||||
}
|
||||
|
||||
return $processed;
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is where a pattern is matched against $content and the matches
|
||||
* are replaced by their respective value.
|
||||
* This function will be called plenty of times, where $content will always
|
||||
* move up 1 character.
|
||||
* If $replacement is a callback, execute it, passing in the match data.
|
||||
* If it's a string, just pass it through.
|
||||
*
|
||||
* @param string $pattern Pattern to match
|
||||
* @param string|callable $replacement Replacement value
|
||||
* @param string $content Content to match pattern against
|
||||
* @param array $match Match data, in PREG_OFFSET_CAPTURE form
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function replacePattern($pattern, $replacement, $content)
|
||||
protected function executeReplacement($replacement, $match)
|
||||
{
|
||||
if (is_callable($replacement)) {
|
||||
return preg_replace_callback($pattern, $replacement, $content, 1, $count);
|
||||
} else {
|
||||
return preg_replace($pattern, $replacement, $content, 1, $count);
|
||||
if (!is_callable($replacement)) {
|
||||
return $replacement;
|
||||
}
|
||||
// convert $match from the PREG_OFFSET_CAPTURE form to the form the callback expects
|
||||
foreach ($match as &$matchItem) {
|
||||
$matchItem = $matchItem[0];
|
||||
}
|
||||
return $replacement($match);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -498,4 +494,12 @@ abstract class Minify
|
||||
throw new IOException('The file "'.$path.'" could not be written to. Check your disk space and file permissions.');
|
||||
}
|
||||
}
|
||||
|
||||
protected static function str_replace_first($search, $replace, $subject) {
|
||||
$pos = strpos($subject, $search);
|
||||
if ($pos !== false) {
|
||||
return substr_replace($subject, $replace, $pos, strlen($search));
|
||||
}
|
||||
return $subject;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user