Skip to content

Commit

Permalink
Improve regex extraction
Browse files Browse the repository at this point in the history
Fixes issue #115
  • Loading branch information
matthiasmullie committed Jun 9, 2016
1 parent 2552412 commit dcdedb0
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 13 deletions.
28 changes: 15 additions & 13 deletions src/JS.php
Expand Up @@ -205,25 +205,27 @@ protected function extractRegex()
$callback = function ($match) use ($minifier) {
$count = count($minifier->extracted);
$placeholder = '/'.$count.'/';
$minifier->extracted[$placeholder] = $match[1];
$minifier->extracted[$placeholder] = $match[0];

return $placeholder;
};

// it's a regex if we can find an opening and (not escaped) closing /,
// include \n because it may be there for a reason
$pattern = '\/.*?(?<!\\\\)(\\\\\\\\)*+\/[gimy]*';

// a regular expression can only be followed by a few operators or some
// of the RegExp methods (a `\` followed by a variable or value is
// likely part of a division, not a regex)
$after = '[,;\)]';
$methods = '\.(exec|test|match|search|replace|split)\(';
$this->registerPattern('/'.$pattern.'\s*(?=('.$after.'|'.$methods.'))/', $callback);

// 1 more edgecase: a regex can be followed by a lot more operators or
// keywords if there's a newline (ASI) in between, where the operator
// actually starts a new statement
// (https://github.com/matthiasmullie/minify/issues/56)
$pattern = '(\/.*?(?<!\\\\)(\\\\\\\\)*+\/\n?)';

// / can't be preceded by variable, value, or similar because then
// it's going to be division
// checking for that is complex, so we'll do inverse:
// * at the beginning of the file, it's not division, but regex
$this->registerPattern('/^\s*\K'.$pattern.'/', $callback);
// * following another operator, it's not division, but regex
$operators = $this->getOperatorsForRegex($this->operatorsBefore, '/');
$operators += $this->getKeywordsForRegex($this->keywordsReserved, '/');
$this->registerPattern('/(?:'.implode('|', $operators).')\s*\K'.$pattern.'/', $callback);
$operators += $this->getOperatorsForRegex($this->keywordsReserved, '/');
$this->registerPattern('/'.$pattern.'\s*\n?(?=\s*('.implode('|', $operators).'))/', $callback);
}

/**
Expand Down
10 changes: 10 additions & 0 deletions tests/js/JSTest.php
Expand Up @@ -88,6 +88,10 @@ public function dataProvider()
'a = b / c; d = e / f',
'a=b/c;d=e/f',
);
$tests[] = array(
'(2 + 4) / 3 + 5 / 1',
'(2+4)/3+5/1',
);

$tests[] = array(
'a=4/
Expand Down Expand Up @@ -704,6 +708,12 @@ function isJSON(){str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[
'function isHtmlNamespace(node){var ns;return typeof node.namespaceURI==UNDEF||((ns=node.namespaceURI)===null||ns=="http://www.w3.org/1999/xhtml")}',
);

// https://github.com/matthiasmullie/minify/issues/115
$tests[] = array(
'if(typeof i[s].token=="string")/keyword|support|storage/.test(i[s].token)&&n.push(i[s].regex);else if(typeof i[s].token=="object")for(var u=0,a=i[s].token.length;u<a;u++)if(/keyword|support|storage/.test(i[s].token[u])){}',
'if(typeof i[s].token=="string")/keyword|support|storage/.test(i[s].token)&&n.push(i[s].regex);else if(typeof i[s].token=="object")for(var u=0,a=i[s].token.length;u<a;u++)if(/keyword|support|storage/.test(i[s].token[u])){}',
);

// update tests' expected results for cross-system compatibility
foreach ($tests as &$test) {
if (!empty($test[1])) {
Expand Down

0 comments on commit dcdedb0

Please sign in to comment.