Index: trunk/phase3/maintenance/minify.php |
— | — | @@ -35,9 +35,12 @@ |
36 | 36 | "Directory for output. If this is not specified, and neither is --outfile, then the\n" . |
37 | 37 | "output files will be sent to the same directories as the input files.", |
38 | 38 | false, true ); |
39 | | - $this->addOption( 'minify-vertical-space', |
40 | | - "Boolean value for minifying the vertical space for javascript.", |
| 39 | + $this->addOption( 'js-statements-on-own-line', |
| 40 | + "Boolean value for putting statements on their own line when minifying JavaScript.", |
41 | 41 | false, true ); |
| 42 | + $this->addOption( 'js-max-line-length', |
| 43 | + "Maximum line length for JavaScript minification.", |
| 44 | + false, true ); |
42 | 45 | $this->mDescription = "Minify a file or set of files.\n\n" . |
43 | 46 | "If --outfile is not specified, then the output file names will have a .min extension\n" . |
44 | 47 | "added, e.g. jquery.js -> jquery.min.js."; |
— | — | @@ -99,7 +102,7 @@ |
100 | 103 | } |
101 | 104 | |
102 | 105 | public function minify( $inPath, $outPath ) { |
103 | | - global $wgResourceLoaderMinifyJSVerticalSpace; |
| 106 | + global $wgResourceLoaderMinifierStatementsOnOwnLine, $wgResourceLoaderMinifierMaxLineLength; |
104 | 107 | |
105 | 108 | $extension = $this->getExtension( $inPath ); |
106 | 109 | $this->output( basename( $inPath ) . ' -> ' . basename( $outPath ) . '...' ); |
— | — | @@ -117,7 +120,10 @@ |
118 | 121 | |
119 | 122 | switch ( $extension ) { |
120 | 123 | case 'js': |
121 | | - $outText = JavaScriptDistiller::stripWhiteSpace( $inText, $this->getOption( 'minify-vertical-space', $wgResourceLoaderMinifyJSVerticalSpace ) ); |
| 124 | + $outText = JavaScriptMinifier::minify( $inText, |
| 125 | + $this->getOption( 'js-statements-on-own-line', $wgResourceLoaderMinifierStatementsOnOwnLine ), |
| 126 | + $this->getOption( 'js-max-line-length', $wgResourceLoaderMinifierMaxLineLength ) |
| 127 | + ); |
122 | 128 | break; |
123 | 129 | case 'css': |
124 | 130 | $outText = CSSMin::minify( $inText ); |
Index: trunk/phase3/includes/resourceloader/ResourceLoader.php |
— | — | @@ -121,6 +121,7 @@ |
122 | 122 | * @return String: Filtered data, or a comment containing an error message |
123 | 123 | */ |
124 | 124 | protected function filter( $filter, $data ) { |
| 125 | + global $wgResourceLoaderMinifierStatementsOnOwnLine, $wgResourceLoaderMinifierMaxLineLength; |
125 | 126 | wfProfileIn( __METHOD__ ); |
126 | 127 | |
127 | 128 | // For empty/whitespace-only data or for unknown filters, don't perform |
— | — | @@ -147,7 +148,10 @@ |
148 | 149 | try { |
149 | 150 | switch ( $filter ) { |
150 | 151 | case 'minify-js': |
151 | | - $result = JavaScriptMinifier::minify( $data ); |
| 152 | + $result = JavaScriptMinifier::minify( $data, |
| 153 | + $wgResourceLoaderMinifierStatementsOnOwnLine, |
| 154 | + $wgResourceLoaderMinifierMaxLineLength |
| 155 | + ); |
152 | 156 | $result .= "\n\n/* cache key: $key */\n"; |
153 | 157 | break; |
154 | 158 | case 'minify-css': |
Index: trunk/phase3/includes/libs/JavaScriptMinifier.php |
— | — | @@ -66,10 +66,16 @@ |
67 | 67 | /** |
68 | 68 | * Returns minified JavaScript code. |
69 | 69 | * |
| 70 | + * NOTE: $maxLineLength isn't a strict maximum. Longer lines will be produced when |
| 71 | + * literals (e.g. quoted strings) longer than $maxLineLength are encountered |
| 72 | + * or when required to guard against semicolon insertion. |
| 73 | + * |
70 | 74 | * @param $s String JavaScript code to minify |
| 75 | + * @param $statementsOnOwnLine Bool Whether to put each statement on its own line |
| 76 | + * @param $maxLineLength Int Maximum length of a single line, or -1 for no maximum. |
71 | 77 | * @return String Minified code |
72 | 78 | */ |
73 | | - public static function minify( $s ) { |
| 79 | + public static function minify( $s, $statementsOnOwnLine = false, $maxLineLength = 1000 ) { |
74 | 80 | // First we declare a few tables that contain our parsing rules |
75 | 81 | |
76 | 82 | // $opChars : characters, which can be combined without whitespace in between them |
— | — | @@ -377,6 +383,23 @@ |
378 | 384 | self::TYPE_LITERAL => true |
379 | 385 | ) |
380 | 386 | ); |
| 387 | + |
| 388 | + // Rules for when newlines should be inserted if |
| 389 | + // $statementsOnOwnLine is enabled. |
| 390 | + // $newlineBefore is checked before switching state, |
| 391 | + // $newlineAfter is checked after |
| 392 | + $newlineBefore = array( |
| 393 | + self::STATEMENT => array( |
| 394 | + self::TYPE_BRACE_CLOSE => true, |
| 395 | + ), |
| 396 | + ); |
| 397 | + $newlineAfter = array( |
| 398 | + self::STATEMENT => array( |
| 399 | + self::TYPE_BRACE_OPEN => true, |
| 400 | + self::TYPE_PAREN_CLOSE => true, |
| 401 | + self::TYPE_SEMICOLON => true, |
| 402 | + ), |
| 403 | + ); |
381 | 404 | |
382 | 405 | // $divStates : Contains all states that can be followed by a division operator |
383 | 406 | $divStates = array( |
— | — | @@ -391,6 +414,7 @@ |
392 | 415 | $out = ''; |
393 | 416 | $pos = 0; |
394 | 417 | $length = strlen( $s ); |
| 418 | + $lineLength = 0; |
395 | 419 | $newlineFound = true; |
396 | 420 | $state = self::STATEMENT; |
397 | 421 | $stack = array(); |
— | — | @@ -492,7 +516,7 @@ |
493 | 517 | } |
494 | 518 | |
495 | 519 | // Now get the token type from our type array |
496 | | - $token = substr( $s, $pos, $end - $pos ); |
| 520 | + $token = substr( $s, $pos, $end - $pos ); // so $end - $pos == strlen( $token ) |
497 | 521 | $type = isset( $tokenTypes[$token] ) ? $tokenTypes[$token] : self::TYPE_LITERAL; |
498 | 522 | |
499 | 523 | if( $newlineFound && isset( $semicolon[$state][$type] ) ) { |
— | — | @@ -500,20 +524,38 @@ |
501 | 525 | // could add the ; token here ourselves, keeping the newline has a few advantages. |
502 | 526 | $out .= "\n"; |
503 | 527 | $state = self::STATEMENT; |
504 | | - } elseif( false /* Put your newline condition here */ ) { |
| 528 | + $lineLength = 0; |
| 529 | + } elseif( $maxLineLength > 0 && $lineLength + $end - $pos > $maxLineLength && |
| 530 | + !isset( $semicolon[$state][$type] ) ) |
| 531 | + { |
| 532 | + // This line would get too long if we added $token, so add a newline first. |
| 533 | + // Only do this if it won't trigger semicolon insertion though. |
505 | 534 | $out .= "\n"; |
| 535 | + $lineLength = 0; |
506 | 536 | // Check, whether we have to separate the token from the last one with whitespace |
507 | 537 | } elseif( !isset( $opChars[$last] ) && !isset( $opChars[$ch] ) ) { |
508 | 538 | $out .= ' '; |
| 539 | + $lineLength++; |
509 | 540 | // Don't accidentally create ++, -- or // tokens |
510 | 541 | } elseif( $last === $ch && ( $ch === '+' || $ch === '-' || $ch === '/' ) ) { |
511 | 542 | $out .= ' '; |
| 543 | + $lineLength++; |
512 | 544 | } |
513 | 545 | |
514 | 546 | $out .= $token; |
| 547 | + $lineLength += $end - $pos; // += strlen( $token ) |
515 | 548 | $last = $s[$end - 1]; |
516 | 549 | $pos = $end; |
517 | 550 | $newlineFound = false; |
| 551 | + |
| 552 | + // Output a newline after the token if required |
| 553 | + // This is checked before AND after switching state |
| 554 | + $newlineAdded = false; |
| 555 | + if ( $statementsOnOwnLine && !$newlineAdded && isset( $newlineBefore[$state][$type] ) ) { |
| 556 | + $out .= "\n"; |
| 557 | + $lineLength = 0; |
| 558 | + $newlineAdded = true; |
| 559 | + } |
518 | 560 | |
519 | 561 | // Now that we have output our token, transition into the new state. |
520 | 562 | if( isset( $push[$state][$type] ) && count( $stack ) < self::STACK_LIMIT ) { |
— | — | @@ -524,6 +566,12 @@ |
525 | 567 | } elseif( isset( $goto[$state][$type] ) ) { |
526 | 568 | $state = $goto[$state][$type]; |
527 | 569 | } |
| 570 | + |
| 571 | + // Check for newline insertion again |
| 572 | + if ( $statementsOnOwnLine && !$newlineAdded && isset( $newlineAfter[$state][$type] ) ) { |
| 573 | + $out .= "\n"; |
| 574 | + $lineLength = 0; |
| 575 | + } |
528 | 576 | } |
529 | 577 | return $out; |
530 | 578 | } |
Index: trunk/phase3/includes/DefaultSettings.php |
— | — | @@ -2505,12 +2505,19 @@ |
2506 | 2506 | $wgResourceLoaderUseESI = false; |
2507 | 2507 | |
2508 | 2508 | /** |
2509 | | - * Enable removal of some of the vertical whitespace (like \r and \n) from |
2510 | | - * JavaScript code when minifying. |
| 2509 | + * Put each statement on its own line when minifying JavaScript. This makes |
| 2510 | + * debugging in non-debug mode a bit easier. |
2511 | 2511 | */ |
2512 | | -$wgResourceLoaderMinifyJSVerticalSpace = false; |
| 2512 | +$wgResourceLoaderMinifierStatementsOnOwnLine = false; |
2513 | 2513 | |
2514 | 2514 | /** |
| 2515 | + * Maximum line length when minifying JavaScript. This is not a hard maximum: |
| 2516 | + * the minifier will try not to produce lines longer than this, but may be |
| 2517 | + * forced to do so in certain cases. |
| 2518 | + */ |
| 2519 | +$wgResourceLoaderMinifierMaxLineLength = 1000; |
| 2520 | + |
| 2521 | +/** |
2515 | 2522 | * Whether to include the mediawiki.legacy JS library (old wikibits.js), and its |
2516 | 2523 | * dependencies |
2517 | 2524 | */ |