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

split on preserved comments before running minification, fixes #222 #320

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 53 additions & 35 deletions src/CSS.php
Original file line number Diff line number Diff line change
Expand Up @@ -295,30 +295,60 @@ protected function importFiles($source, $content)
*/
public function execute($path = null, $parents = array())
{
$content = '';
$preservecommentpattern = '/(
# 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';

// loop CSS data (raw data and files)
foreach ($this->data as $source => $css) {
/*
* Let's first take out strings & comments, since we can't just
* remove whitespace anywhere. If whitespace occurs inside a string,
* we should leave it alone. E.g.:
* p { content: "a test" }
*/
$this->extractStrings();
$this->stripComments();
$this->extractCalcs();
$css = $this->replace($css);

$css = $this->stripWhitespace($css);
$css = $this->shortenColors($css);
$css = $this->shortenZeroes($css);
$css = $this->shortenFontWeights($css);
$css = $this->stripEmptyTags($css);

// restore the string we've extracted earlier
$css = $this->restoreExtractedData($css);

// Split JS on special comments.
$chunks = preg_split($preservecommentpattern, $css, -1, PREG_SPLIT_DELIM_CAPTURE );
$processed = [];
for ($i = 0; $i < count($chunks); $i += 2) {
$code = $chunks[$i];
$comment = '';
if (isset($chunks[$i + 1])) {
$comment = $chunks[$i + 1];
}
/*
* Let's first take out strings & other comments, since we can't just
* remove whitespace anywhere. If whitespace occurs inside a string,
* we should leave it alone. E.g.:
* p { content: "a test" }
*/
$this->extractStrings();
$this->stripComments();
$this->extractCalcs();
$code = $this->replace($code);

$code = $this->stripWhitespace($code);
$code = $this->shortenColors($code);
$code = $this->shortenZeroes($code);
$code = $this->shortenFontWeights($code);
$code = $this->stripEmptyTags($code);

// restore the string we've extracted earlier
$code = $this->restoreExtractedData($code);

$processed[] = $code;
$processed[] = $comment;
}
$css = implode($processed);
$source = is_int($source) ? '' : $source;
$parents = $source ? array_merge($parents, array($source)) : $parents;
$css = $this->combineImports($source, $css, $parents);
Expand All @@ -335,9 +365,9 @@ public function execute($path = null, $parents = array())
$css = $this->move($converter, $css);

// combine css
$content .= $css;
$content[] = $css;
}

$content = implode($content);
$content = $this->moveImportsToTop($content);

return $content;
Expand Down Expand Up @@ -627,17 +657,6 @@ protected function stripEmptyTags($content)
*/
protected function stripComments()
{
// 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];

return $placeholder;
};
$this->registerPattern('/\n?\/\*(!|.*?@license|.*?@preserve).*?\*\/\n?/s', $callback);

$this->registerPattern('/\/\*.*?\*\//s', '');
}

Expand Down Expand Up @@ -708,7 +727,6 @@ protected function extractCalcs()
return $placeholder.$rest;
};

$this->registerPattern('/calc(\(.+?)(?=$|;|}|calc\()/', $callback);
$this->registerPattern('/calc(\(.+?)(?=$|;|}|calc\()/m', $callback);
}

Expand Down
68 changes: 45 additions & 23 deletions src/JS.php
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,6 @@ public function __construct()
*/
public function execute($path = null)
{
$content = '';

/*
* Let's first take out strings, comments and regular expressions.
* All of these can contain JS code-like characters, and we should make
Expand All @@ -163,23 +161,57 @@ public function execute($path = null)
$this->stripComments();
$this->extractRegex();

$preservecommentpattern = '/(
# 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';
// loop files
foreach ($this->data as $source => $js) {
// take out strings, comments & regex (for which we've registered
// the regexes just a few lines earlier)
$js = $this->replace($js);

$js = $this->propertyNotation($js);
$js = $this->shortenBools($js);
$js = $this->stripWhitespace($js);
// Split JS on special comments.
$chunks = preg_split($preservecommentpattern, $js, -1, PREG_SPLIT_DELIM_CAPTURE );
$processed = [];
for ($i = 0; $i < count($chunks); $i += 2) {
$code = $chunks[$i];
$comment = '';
if (isset($chunks[$i + 1])) {
$comment = $chunks[$i + 1];
}

// take out strings, other comments & regex (for which we've registered
// the regexes just a few lines earlier)
$code = $this->replace($code);

// combine js: separating the scripts by a ;
$content .= $js.";";
}
$code = $this->propertyNotation($code);
$code = $this->shortenBools($code);
$code = $this->stripWhitespace($code);

$processed[] = $code;
$processed[] = $comment;
}
$file = implode($processed);
// ASI at end of file, will be added back if concatenated later.
$file = preg_replace('/;$/s', '', $file);

$files[] = $file;
}
$content = implode(';', $files);
// clean up leftover `;`s from the combination of multiple scripts
$content = ltrim($content, ';');
$content = (string) substr($content, 0, -1);

/*
* Earlier, we extracted strings & regular expressions and replaced them
Expand All @@ -195,17 +227,7 @@ public function execute($path = null)
*/
protected function stripComments()
{
// 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];

return $placeholder;
};
// multi-line comments
$this->registerPattern('/\n?\/\*(!|.*?@license|.*?@preserve).*?\*\/\n?/s', $callback);
$this->registerPattern('/\/\*.*?\*\//s', '');

// single-line comments
Expand Down Expand Up @@ -432,7 +454,7 @@ protected function stripWhitespace($content)
* script: ASI will kick in here & we're all about minifying.
* Semicolons at beginning of the file don't make any sense either.
*/
$content = preg_replace('/;(\}|$)/s', '\\1', $content);
$content = preg_replace('/;(\})/s', '\\1', $content);
$content = ltrim($content, ';');

// get rid of remaining whitespace af beginning/end
Expand Down
15 changes: 8 additions & 7 deletions src/Minify.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public function add($data /* $data = null, ... */)
* @param string|string[] $data
*
* @return static
*
*
* @throws IOException
*/
public function addFile($data /* $data = null, ... */)
Expand Down Expand Up @@ -268,7 +268,7 @@ protected function registerPattern($pattern, $replacement = '')
*/
protected function replace($content)
{
$processed = '';
$processed = [];
$positions = array_fill(0, count($this->patterns), -1);
$matches = array();

Expand Down Expand Up @@ -307,7 +307,7 @@ protected function replace($content)

// no more matches to find: everything's been processed, break out
if (!$matches) {
$processed .= $content;
$processed[] = $content;
break;
}

Expand All @@ -317,6 +317,7 @@ protected function replace($content)
$discardLength = min($positions);
$firstPattern = array_search($discardLength, $positions);
$match = $matches[$firstPattern][0][0];
$matchlen = strlen($match);

// execute the pattern that matches earliest in the content string
list($pattern, $replacement) = $this->patterns[$firstPattern];
Expand All @@ -325,22 +326,22 @@ protected function replace($content)
// 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));
$unmatched = (string) substr($content, strpos($content, $match) + $matchlen);

// move the replaced part to $processed and prepare $content to
// again match batch of patterns against
$processed .= substr($replacement, 0, strlen($replacement) - strlen($unmatched));
$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);
$positions[$i] -= $discardLength + $matchlen;
}
}

return $processed;
return implode($processed);
}

/**
Expand Down