$wgResourceLoaderUseESI = false;
/**
- * Enable removal of some of the vertical whitespace (like \r and \n) from
- * JavaScript code when minifying.
+ * Put each statement on its own line when minifying JavaScript. This makes
+ * debugging in non-debug mode a bit easier.
*/
-$wgResourceLoaderMinifyJSVerticalSpace = false;
+$wgResourceLoaderMinifierStatementsOnOwnLine = false;
+
+/**
+ * Maximum line length when minifying JavaScript. This is not a hard maximum:
+ * the minifier will try not to produce lines longer than this, but may be
+ * forced to do so in certain cases.
+ */
+$wgResourceLoaderMinifierMaxLineLength = 1000;
/**
* Whether to include the mediawiki.legacy JS library (old wikibits.js), and its
/**
* Returns minified JavaScript code.
*
+ * NOTE: $maxLineLength isn't a strict maximum. Longer lines will be produced when
+ * literals (e.g. quoted strings) longer than $maxLineLength are encountered
+ * or when required to guard against semicolon insertion.
+ *
* @param $s String JavaScript code to minify
+ * @param $statementsOnOwnLine Bool Whether to put each statement on its own line
+ * @param $maxLineLength Int Maximum length of a single line, or -1 for no maximum.
* @return String Minified code
*/
- public static function minify( $s ) {
+ public static function minify( $s, $statementsOnOwnLine = false, $maxLineLength = 1000 ) {
// First we declare a few tables that contain our parsing rules
// $opChars : characters, which can be combined without whitespace in between them
self::TYPE_LITERAL => true
)
);
+
+ // Rules for when newlines should be inserted if
+ // $statementsOnOwnLine is enabled.
+ // $newlineBefore is checked before switching state,
+ // $newlineAfter is checked after
+ $newlineBefore = array(
+ self::STATEMENT => array(
+ self::TYPE_BRACE_CLOSE => true,
+ ),
+ );
+ $newlineAfter = array(
+ self::STATEMENT => array(
+ self::TYPE_BRACE_OPEN => true,
+ self::TYPE_PAREN_CLOSE => true,
+ self::TYPE_SEMICOLON => true,
+ ),
+ );
// $divStates : Contains all states that can be followed by a division operator
$divStates = array(
$out = '';
$pos = 0;
$length = strlen( $s );
+ $lineLength = 0;
$newlineFound = true;
$state = self::STATEMENT;
$stack = array();
}
// Now get the token type from our type array
- $token = substr( $s, $pos, $end - $pos );
+ $token = substr( $s, $pos, $end - $pos ); // so $end - $pos == strlen( $token )
$type = isset( $tokenTypes[$token] ) ? $tokenTypes[$token] : self::TYPE_LITERAL;
if( $newlineFound && isset( $semicolon[$state][$type] ) ) {
// could add the ; token here ourselves, keeping the newline has a few advantages.
$out .= "\n";
$state = self::STATEMENT;
- } elseif( false /* Put your newline condition here */ ) {
+ $lineLength = 0;
+ } elseif( $maxLineLength > 0 && $lineLength + $end - $pos > $maxLineLength &&
+ !isset( $semicolon[$state][$type] ) )
+ {
+ // This line would get too long if we added $token, so add a newline first.
+ // Only do this if it won't trigger semicolon insertion though.
$out .= "\n";
+ $lineLength = 0;
// Check, whether we have to separate the token from the last one with whitespace
} elseif( !isset( $opChars[$last] ) && !isset( $opChars[$ch] ) ) {
$out .= ' ';
+ $lineLength++;
// Don't accidentally create ++, -- or // tokens
} elseif( $last === $ch && ( $ch === '+' || $ch === '-' || $ch === '/' ) ) {
$out .= ' ';
+ $lineLength++;
}
$out .= $token;
+ $lineLength += $end - $pos; // += strlen( $token )
$last = $s[$end - 1];
$pos = $end;
$newlineFound = false;
+
+ // Output a newline after the token if required
+ // This is checked before AND after switching state
+ $newlineAdded = false;
+ if ( $statementsOnOwnLine && !$newlineAdded && isset( $newlineBefore[$state][$type] ) ) {
+ $out .= "\n";
+ $lineLength = 0;
+ $newlineAdded = true;
+ }
// Now that we have output our token, transition into the new state.
if( isset( $push[$state][$type] ) && count( $stack ) < self::STACK_LIMIT ) {
} elseif( isset( $goto[$state][$type] ) ) {
$state = $goto[$state][$type];
}
+
+ // Check for newline insertion again
+ if ( $statementsOnOwnLine && !$newlineAdded && isset( $newlineAfter[$state][$type] ) ) {
+ $out .= "\n";
+ $lineLength = 0;
+ }
}
return $out;
}
* @return String: Filtered data, or a comment containing an error message
*/
protected function filter( $filter, $data ) {
+ global $wgResourceLoaderMinifierStatementsOnOwnLine, $wgResourceLoaderMinifierMaxLineLength;
wfProfileIn( __METHOD__ );
// For empty/whitespace-only data or for unknown filters, don't perform
try {
switch ( $filter ) {
case 'minify-js':
- $result = JavaScriptMinifier::minify( $data );
+ $result = JavaScriptMinifier::minify( $data,
+ $wgResourceLoaderMinifierStatementsOnOwnLine,
+ $wgResourceLoaderMinifierMaxLineLength
+ );
$result .= "\n\n/* cache key: $key */\n";
break;
case 'minify-css':
"Directory for output. If this is not specified, and neither is --outfile, then the\n" .
"output files will be sent to the same directories as the input files.",
false, true );
- $this->addOption( 'minify-vertical-space',
- "Boolean value for minifying the vertical space for javascript.",
+ $this->addOption( 'js-statements-on-own-line',
+ "Boolean value for putting statements on their own line when minifying JavaScript.",
+ false, true );
+ $this->addOption( 'js-max-line-length',
+ "Maximum line length for JavaScript minification.",
false, true );
$this->mDescription = "Minify a file or set of files.\n\n" .
"If --outfile is not specified, then the output file names will have a .min extension\n" .
}
public function minify( $inPath, $outPath ) {
- global $wgResourceLoaderMinifyJSVerticalSpace;
+ global $wgResourceLoaderMinifierStatementsOnOwnLine, $wgResourceLoaderMinifierMaxLineLength;
$extension = $this->getExtension( $inPath );
$this->output( basename( $inPath ) . ' -> ' . basename( $outPath ) . '...' );
switch ( $extension ) {
case 'js':
- $outText = JavaScriptDistiller::stripWhiteSpace( $inText, $this->getOption( 'minify-vertical-space', $wgResourceLoaderMinifyJSVerticalSpace ) );
+ $outText = JavaScriptMinifier::minify( $inText,
+ $this->getOption( 'js-statements-on-own-line', $wgResourceLoaderMinifierStatementsOnOwnLine ),
+ $this->getOption( 'js-max-line-length', $wgResourceLoaderMinifierMaxLineLength )
+ );
break;
case 'css':
$outText = CSSMin::minify( $inText );