Version 4.7.23
This commit is contained in:
1 parent
7124a02564
commit
25ddeb65d6
1791 files changed
+76990
-44452
No files matched your search
+25
@@ -0,0 +1,25 @@
|
||||
# Contributing
|
||||
|
||||
When you make a contribution, please check:
|
||||
|
||||
- If your code runs using PHP 5.6 (and up)
|
||||
- If the tests run
|
||||
|
||||
## Run the tests
|
||||
|
||||
To run both unit tests and real-life tests on actual libraries, do
|
||||
|
||||
/usr/bin/php7.3 ./vendor/bin/phpunit
|
||||
|
||||
To just run the unit tests, do
|
||||
|
||||
/usr/bin/php7.3 ./vendor/bin/phpunit --group unit
|
||||
|
||||
To just run the real-life tests, do
|
||||
|
||||
/usr/bin/php7.3 ./vendor/bin/phpunit --group libraries
|
||||
|
||||
The specified version of the PHP runtime is only important for the real-life tests, which test execution time.
|
||||
|
||||
If you find that your computer is slower or faster than mine, change the maxDuration values of the real-life tests.
|
||||
Then, after you made your change, check that it hasn't made the code slower.
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Patrick van Bergen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
|
||||
namespace Garfix\JsMinify;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* This minifier removes whitespace and comments from a Javascript string.
|
||||
*
|
||||
* See docs for information.
|
||||
*
|
||||
* @author Patrick van Bergen
|
||||
*/
|
||||
class Minifier
|
||||
{
|
||||
const FLAGGED_COMMENTS = 'flaggedComments';
|
||||
|
||||
protected static $defaultOptions = [
|
||||
self::FLAGGED_COMMENTS => true
|
||||
];
|
||||
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* Processes a javascript string and returns only the required characters,
|
||||
* stripping out all unneeded whitespace and comments.
|
||||
*
|
||||
* @param string $js The raw javascript to be minified
|
||||
* @param array $options Various runtime options in an associative array
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function minify($js, $options = [])
|
||||
{
|
||||
$minifier = new Minifier($options);
|
||||
return $minifier->minifyUsingCallbacks($js);
|
||||
}
|
||||
|
||||
public function __construct($options)
|
||||
{
|
||||
$this->options = array_merge(static::$defaultOptions, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $js The raw javascript to be minified
|
||||
* @return string
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function minifyUsingCallbacks($js)
|
||||
{
|
||||
$e = MinifierExpressions::get();
|
||||
|
||||
// treat all newlines as unix newlines, to keep working with newlines simple
|
||||
$shrunkText = str_replace(["\r\n", "\r"], ["\n", "\n"], $js);
|
||||
|
||||
// remove comments
|
||||
$exp1 = "~(?:" . implode("|", $e->tokenExpressions) . ")~su";
|
||||
$shrunkText = preg_replace_callback($exp1, [$this, 'removeComments'], $shrunkText);
|
||||
MinifierError::checkRegexpError();
|
||||
|
||||
// rewrite blocks by inserting whitespace placeholders
|
||||
$exp2 = "~(?:" . implode("|", $e->allExpressions) . ")~su";
|
||||
$shrunkText = preg_replace_callback($exp2, [$this, 'processBlocks'], $shrunkText);
|
||||
MinifierError::checkRegexpError();
|
||||
|
||||
// remove all remaining space (without the newlines)
|
||||
$shrunkText = preg_replace("~" . $e->someWhitespace . "~", '', $shrunkText);
|
||||
MinifierError::checkRegexpError();
|
||||
|
||||
// reduce consecutive newlines to single one
|
||||
$shrunkText = preg_replace("~[\\n]+~", "\n", $shrunkText);
|
||||
MinifierError::checkRegexpError();
|
||||
|
||||
// remove newlines that may safely be removed
|
||||
foreach ($e->safeNewlines as $safeNewline) {
|
||||
$shrunkText = preg_replace("~" . $safeNewline . "~", "", $shrunkText);
|
||||
MinifierError::checkRegexpError();
|
||||
}
|
||||
|
||||
// replace whitespace placeholders by their original whitespace
|
||||
$shrunkText = str_replace($e->whitespaceArrayPlaceholders, $e->whitespaceArrayNewline, $shrunkText);
|
||||
MinifierError::checkRegexpError();
|
||||
|
||||
// remove leading and trailing whitespace
|
||||
return trim($shrunkText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all comments that need to be removed
|
||||
* The newlines that are added here may later be removed again
|
||||
*
|
||||
* @param $matches
|
||||
* @return mixed|string
|
||||
*/
|
||||
protected function removeComments($matches)
|
||||
{
|
||||
$e = MinifierExpressions::get();
|
||||
|
||||
// the fully matching text
|
||||
$match = $matches[0];
|
||||
|
||||
if (!empty($matches['lineComment'])) {
|
||||
// not empty because this might glue words together
|
||||
return "\n";
|
||||
}
|
||||
if (!empty($matches['starComment'])) {
|
||||
|
||||
// create a version without leading and trailing whitespace
|
||||
$trimmed = trim($match, $e->whitespaceCharsNewline);
|
||||
|
||||
switch ($trimmed[2]) {
|
||||
case '@':
|
||||
// IE conditional comment
|
||||
return $match;
|
||||
case '!':
|
||||
if ($this->options[self::FLAGGED_COMMENTS]) {
|
||||
// option says: leave flagged comments in
|
||||
return $match;
|
||||
}
|
||||
}
|
||||
// multi line comment; not empty because this might glue words together
|
||||
return "\n";
|
||||
}
|
||||
|
||||
// leave other matches unchanged
|
||||
return $match;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the code for all blocks (they contain whitespace that should be conserved)
|
||||
* No early returns: all code must reach `end` and have the whitespace replaced by placeholders
|
||||
*
|
||||
* @param $matches
|
||||
* @return array|mixed|string|string[]
|
||||
*/
|
||||
protected function processBlocks($matches)
|
||||
{
|
||||
$e = MinifierExpressions::get();
|
||||
|
||||
// the fully matching text
|
||||
$match = $matches[0];
|
||||
|
||||
// create a version without leading and trailing whitespace
|
||||
$trimmed = trim($match, $e->whitespaceCharsNewline);
|
||||
|
||||
// Should be handled before optional whitespace
|
||||
if (!empty($matches['requiredSpace'])) {
|
||||
$match = strpos($matches['requiredSpace'], "\n") === false ? " " : "\n";
|
||||
goto end;
|
||||
}
|
||||
// + followed by +, or - followed by -
|
||||
if (!empty($matches['plus']) || !empty($matches['min'])) {
|
||||
$match = ' ';
|
||||
goto end;
|
||||
}
|
||||
if (!empty($matches['doubleQuote'])) {
|
||||
// remove line continuation
|
||||
$match = str_replace("\\\n", "", $match);
|
||||
goto end;
|
||||
}
|
||||
if (!empty($matches['starComment'])) {
|
||||
switch ($trimmed[2]) {
|
||||
case '@':
|
||||
// IE conditional comment
|
||||
$match = $trimmed;
|
||||
goto end;
|
||||
case '!':
|
||||
if ($this->options[self::FLAGGED_COMMENTS]) {
|
||||
// ensure newlines before and after
|
||||
$match = "\n" . $trimmed . "\n";
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
// simple multi line comment; will have been removed in the first step
|
||||
goto end;
|
||||
}
|
||||
if (!empty($matches['regexp'])) {
|
||||
// regular expression
|
||||
// only if the space after the regexp contains a newline, keep it
|
||||
preg_match("~^" . $e->regexp . "(?P<post>" . $e->optionalWhitespaceNewline . ")" . "$~su",
|
||||
$match, $newMatches);
|
||||
$postfix = strpos($newMatches['post'], "\n") === false ? "" : "\n";
|
||||
$match = $trimmed . $postfix;
|
||||
goto end;
|
||||
}
|
||||
|
||||
end:
|
||||
|
||||
return str_replace($e->whitespaceArrayNewline, $e->whitespaceArrayPlaceholders, $match);
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Garfix\JsMinify;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* @author Patrick van Bergen
|
||||
*/
|
||||
class MinifierError
|
||||
{
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function checkRegexpError()
|
||||
{
|
||||
$error = preg_last_error();
|
||||
if ($error === 0) { return; }
|
||||
|
||||
$msg = "";
|
||||
switch ($error) {
|
||||
case PREG_INTERNAL_ERROR: $msg = "Internal error (no specified)"; break;
|
||||
case PREG_BACKTRACK_LIMIT_ERROR: $msg = "Backtrace limit error"; break;
|
||||
case PREG_RECURSION_LIMIT_ERROR: $msg = "Recursion limit error"; break;
|
||||
case PREG_BAD_UTF8_ERROR: $msg = "Bad utf-8 error"; break;
|
||||
case PREG_BAD_UTF8_OFFSET_ERROR: $msg = "Bad utf-8 offset error"; break;
|
||||
case 6 /* PREG_JIT_STACKLIMIT_ERROR */: $msg = "JIT stack limit error"; break;
|
||||
}
|
||||
throw new RuntimeException("A regular expression error occurred: " . $msg);
|
||||
}
|
||||
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
namespace Garfix\JsMinify;
|
||||
|
||||
/**
|
||||
* @author Patrick van Bergen
|
||||
*/
|
||||
class MinifierExpressions
|
||||
{
|
||||
protected static $expressions = null;
|
||||
|
||||
public $tokenExpressions = [];
|
||||
public $allExpressions = [];
|
||||
public $safeNewlines = [];
|
||||
public $whitespaceArrayPlaceholders = [];
|
||||
public $whitespaceArrayNewline;
|
||||
public $whitespaceCharsNewline;
|
||||
public $optionalWhitespaceNewline;
|
||||
public $someWhitespace;
|
||||
public $regexp;
|
||||
|
||||
/**
|
||||
* @return MinifierExpressions
|
||||
*/
|
||||
public static function get() {
|
||||
if (self::$expressions === null) {
|
||||
self::$expressions = self::create();
|
||||
}
|
||||
return self::$expressions;
|
||||
}
|
||||
|
||||
protected static function create() {
|
||||
|
||||
$e = new MinifierExpressions();
|
||||
|
||||
// white space (includes space and tab) https://262.ecma-international.org/6.0/#sec-white-space
|
||||
$whitespaceArray = [" ", "\t"];
|
||||
$e->whitespaceArrayNewline = array_merge($whitespaceArray, ["\n"]);
|
||||
// placeholders are unused code points; see also https://en.wikipedia.org/wiki/Private_Use_Areas
|
||||
$e->whitespaceArrayPlaceholders = ["\x{E000}", "\x{E001}", "\x{E002}"];
|
||||
|
||||
$whitespaceChars = implode("", $whitespaceArray);
|
||||
$e->whitespaceCharsNewline = "\n" . $whitespaceChars;
|
||||
|
||||
$whitespace = "[" . $whitespaceChars . "]";
|
||||
$whitespaceNewline = "[" . $e->whitespaceCharsNewline . "]";
|
||||
|
||||
// possessive quantifier (++) is needed when used in combination with look ahead
|
||||
$e->someWhitespace = $whitespace . "++";
|
||||
$someWhitespaceNewline = $whitespaceNewline . "++";
|
||||
$optionalWhitespace = $whitespace . "*+";
|
||||
$e->optionalWhitespaceNewline = $whitespaceNewline . "*+";
|
||||
|
||||
// a single escaped character in a regexp, like \\ or \n (may not be newline)
|
||||
$regexEscape = "\\\\[^\n]";
|
||||
// regexp character class: [..] with escape characters
|
||||
// [^\n\\]] is any non-] character (also not be newline)
|
||||
$characterClass = "\\[(?:" . $regexEscape . "|[^\n\\]])*+\\]";
|
||||
// regexp: /..(..)../i with character class and escaped characters
|
||||
// [^\n/] is any non-slash character (also not be newline)
|
||||
$e->regexp = "/(?:" . $regexEscape . "|" . $characterClass . "|[^\n/])++/[igmus]*+";
|
||||
|
||||
// characters than can form a word; these characters should not be joined together by the removal of whitespace
|
||||
$word = "[a-zA-Z0-9_\$\x{0080}-\x{FFFF}]";
|
||||
|
||||
// note: ! is not infix and not safe
|
||||
$safeOperators = ",\.&|*/%=<>?:";
|
||||
$openingBrackets = "({[";
|
||||
$closingBrackets = ")}\\]";
|
||||
$expressionClosingBracket = ")";
|
||||
$blockOpeningBracket = "{";
|
||||
|
||||
// newlines that may be safely removed
|
||||
$e->safeNewlines = [
|
||||
// newline preceded by opening bracket or operator
|
||||
"(?<=[;" . $openingBrackets . $safeOperators . "])" . "\n",
|
||||
// newline followed by closing bracket or operator
|
||||
"\n" . "(?=[;" . $closingBrackets. $safeOperators . "])",
|
||||
// ( \n [
|
||||
"(?<=[" . $openingBrackets . "])" . "\n" . "(?=[" . $openingBrackets . "])",
|
||||
// ] \n )
|
||||
"(?<=[" . $closingBrackets . "])" . "\n" . "(?=[" . $closingBrackets . "])",
|
||||
// } \n (
|
||||
"(?<=[" . $expressionClosingBracket . "])" . "\n" . "(?=[" . $blockOpeningBracket . "])",
|
||||
];
|
||||
|
||||
// these expression must always be present, because they keep tokens that contain whitespace together
|
||||
$e->tokenExpressions = [
|
||||
// /** comment */
|
||||
'starComment' => "(?<starComment>" . "/\*.*?\*/" . $e->optionalWhitespaceNewline . ")",
|
||||
// // comment
|
||||
'lineComment' => "(?<lineComment>//[^\n]*" . $e->optionalWhitespaceNewline . ")",
|
||||
// regular expression
|
||||
'regexp' =>
|
||||
"(?<regexp>" .
|
||||
// if there's whitespace, match it all
|
||||
$e->regexp . $optionalWhitespace .
|
||||
// to distinguish a regexp from a sequence of dividers (i.e.: x / y / z):
|
||||
"(?:" .
|
||||
// it is followed by a newline; add it to the match
|
||||
"\n" .
|
||||
"|" .
|
||||
// it is followed by any of these characters
|
||||
"(?=[;,\.)])" .
|
||||
")" .
|
||||
")",
|
||||
// "double quotes"
|
||||
'double' => '(?<doubleQuote>"(?:\\\\.|[^"])*+")',
|
||||
// 'single quotes'
|
||||
'single' => "(?<singleQuote>'(?:\\\\.|[^'])*+')",
|
||||
// `template literal`
|
||||
'template' => "(?<templateLiteral>`(?:\\\\.|[^`])*+`)",
|
||||
];
|
||||
|
||||
$specificExpressions = [
|
||||
// a sequence of - and -- operators; i.e. a - --b; b-- -c; d-- - -e; f - -g
|
||||
'min' => "(?<=-)(?<min>" . $someWhitespaceNewline . ")(?=-)",
|
||||
// a sequence of + and ++ operators
|
||||
'plus' => "(?<=\+)(?<plus>" . $someWhitespaceNewline . ")(?=\+)",
|
||||
// whitespace both preceded and succeeded by a word char
|
||||
'requiredSpace' => "(?<=" . $word . ")" . "(?<requiredSpace>" . $someWhitespaceNewline . ")" . "(?=" . $word . ")"
|
||||
];
|
||||
|
||||
$e->allExpressions = array_merge($e->tokenExpressions, $specificExpressions);
|
||||
|
||||
return $e;
|
||||
}
|
||||
}
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
# js-minify
|
||||
|
||||
A fast Javascript minifier that removes unnecessary whitespace and comments
|
||||
|
||||
## Installation
|
||||
|
||||
If you are using Composer, use
|
||||
|
||||
composer require garfix/js-minify
|
||||
|
||||
## Use
|
||||
|
||||
The simplest use of the library comes down to this:
|
||||
|
||||
$minifiedJs = \Garfix\JsMinify\Minifier::minify($js);
|
||||
|
||||
Where `$js` contains the unprocessed code and `$minifiedJs` holds the minified version.
|
||||
|
||||
If you want to change the default options, use `minify($js, $options)`, where `$options` is an array of one or more of the following:
|
||||
|
||||
* `\Garfix\JsMinify\Minifier::FLAGGED_COMMENTS` (bool, default: `true`) When set to `false`, `/*! ... */` flagged comments are removed as well.
|
||||
|
||||
## Background
|
||||
|
||||
I started this library because I believed [JShrink](https://github.com/tedious/JShrink) could be made much faster by the use of dedicated regular expressions. This turned out to be true. It is about 10x faster on PHP 7 and 5x faster on PHP 8.
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "garfix/js-minify",
|
||||
"description": "A fast Javascript minifier that removes unnecessary whitespace and comments",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Garfix\\": "src/"
|
||||
}
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Patrick van Bergen",
|
||||
"email": "patrick.vanbergen@gmail.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.6"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.5"
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user