Changed minification engine (closed #4)

This commit is contained in:
Marcin Kurczewski 2014-09-11 11:42:30 +02:00
parent 6ce7beffd2
commit a7d4490b4f
7 changed files with 131 additions and 143 deletions

View file

@ -1,8 +1,6 @@
{
"require": {
"mnapoli/php-di": "~4.0",
"linkorb/jsmin-php": "1.*",
"natxet/CssMin": "3.*",
"phpcheckstyle/phpcheckstyle": "~0.14"
}
}

View file

@ -1,16 +1,54 @@
var path = require('path');
var fs = require('fs');
var ini = require('ini');
module.exports = function(grunt) {
var phpCheckStyleConfigPath = path.join(path.resolve(), 'phpcheckstyle.cfg');
var phpSourcesDir = path.join(path.resolve(), 'src');
var jsSourcesDir = path.join(path.resolve(), 'public_html/js');
var cssSourcesDir = path.join(path.resolve(), 'public_html/css');
var templatesDir = path.join(path.resolve(), 'public_html/templates');
var config = readConfig([
path.join(path.resolve(), 'data/config.ini'),
path.join(path.resolve(), 'data/local.ini')
]);
function readConfig(configPaths) {
var iniContent = '';
for (var i = 0; i < configPaths.length; i ++) {
var configPath = configPaths[i];
if (fs.existsSync(configPath)) {
iniContent += fs.readFileSync(configPath, 'utf-8');
}
}
var config = ini.parse(iniContent);
return config;
}
function readTemplates() {
var templatePaths = grunt.file.expand(templatesDir + '/**/*.tpl');
var templates = {};
for (var i = 0; i < templatePaths.length; i ++) {
var templatePath = templatePaths[i];
templates[path.basename(templatePath)] = fs.readFileSync(templatePath);
}
return templates;
}
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
phpCheckStyleConfigPath: path.join(path.resolve(), 'phpcheckstyle.cfg'),
phpSourcesDir: path.join(path.resolve(), 'src'),
jsSourcesDir: path.join(path.resolve(), 'public_html'),
phpCheckStyleConfigPath: phpCheckStyleConfigPath,
phpSourcesDir: phpSourcesDir,
jsSourcesDir: jsSourcesDir,
cssSourcesDir: cssSourcesDir,
config: config,
jshint: {
files: ['<%= jsSourcesDir %>/**/*.js'],
files: [jsSourcesDir + '/**/*.js'],
options: {
globals: {
console: true,
@ -45,11 +83,66 @@ module.exports = function(grunt) {
command: 'phpunit --strict --bootstrap src/AutoLoader.php tests/',
},
},
cssmin: {
combine: {
files: {
'public_html/app.min.css': [cssSourcesDir + '/**/*.css'],
},
},
},
uglify: {
dist: {
options: {
mangle: false, //breaks dependency injection
sourceMap: true,
},
files: {
'public_html/app.min.js': [].concat(
[jsSourcesDir + '/DI.js'],
grunt.file.expand({
filter: function(src) {
return !src.match(/(DI|Bootstrap)\.js/);
}
}, jsSourcesDir + '/**/*.js'),
[jsSourcesDir + '/Bootstrap.js']),
},
},
},
processhtml: {
options: {
data: {
serviceName: config.basic.serviceName,
templates: readTemplates(),
timestamp: grunt.template.today('isoDateTime'),
}
},
dist: {
files: {
'public_html/app.min.html': ['public_html/index.html']
}
}
},
});
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-processhtml');
grunt.loadNpmTasks('grunt-shell');
grunt.registerTask('default', ['jshint', 'shell']);
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.registerTask('default', ['checkstyle', 'tests']);
grunt.registerTask('checkstyle', ['jshint', 'shell:phpcheckstyle']);
grunt.registerTask('tests', ['shell:tests']);
grunt.registerTask('clean', function() {
fs.unlink('public_html/app.min.html');
fs.unlink('public_html/app.min.js');
fs.unlink('public_html/app.min.js.map');
fs.unlink('public_html/app.min.css');
});
grunt.registerTask('build', ['clean', 'uglify', 'cssmin', 'processhtml']);
};

View file

@ -3,7 +3,11 @@
"version": "0.0.0",
"devDependencies": {
"requirejs": "*",
"ini": "*",
"grunt": "~0.4.5",
"grunt-processhtml": "*",
"grunt-contrib-uglify": "*",
"grunt-contrib-cssmin": "*",
"grunt-contrib-jshint": "~0.10.0",
"grunt-shell": "~1.1.1"
}

View file

@ -1 +1,4 @@
index-compiled.html
app.min.html
app.min.js
app.min.js.map
app.min.css

View file

@ -1,4 +1,4 @@
DirectoryIndex index-compiled.html
DirectoryIndex app.min.html
DirectoryIndex index.html
RewriteEngine On

View file

@ -1,133 +0,0 @@
<?php
define('DS', DIRECTORY_SEPARATOR);
require_once __DIR__ . DS . '..' . DS . 'vendor' . DS . 'autoload.php';
class Compressor
{
public static function css($content)
{
return CssMin::minify($content);
}
public static function js($content)
{
return JSMin::minify($content);
}
public static function html($html)
{
$illegalTags = ['script', 'link', 'textarea', 'pre'];
$chunks = preg_split( '/(<(' . join('|', $illegalTags) . ')(?:\/|.*?\/\2)>)/ms', $html, -1,
PREG_SPLIT_DELIM_CAPTURE);
$buffer = '';
foreach ($chunks as $chunk)
{
if (in_array($chunk, $illegalTags))
continue;
if (preg_match('/^<(' . join('|', $illegalTags) . ')/', $chunk))
{
$buffer .= $chunk;
continue;
}
# remove new lines & tabs
$chunk = preg_replace( '/[\\n\\r\\t]+/', ' ', $chunk);
# remove extra whitespace
$chunk = preg_replace( '/\\s{2,}/', ' ', $chunk);
# remove inter-tag whitespace
$chunk = preg_replace( '/>\\s</', '><', $chunk);
# remove CSS & JS comments
$chunk = preg_replace( '/\\/\\*.*?\\*\\//i', '', $chunk);
$buffer .= $chunk;
}
return $buffer;
}
}
class IndexBuilder
{
public static function build()
{
$html = file_get_contents(__DIR__ . DS . 'index.html');
self::includeTemplates($html);
self::minifyScripts($html);
self::minifyStylesheets($html);
return $html;
}
private static function injectBody(&$html, $text)
{
$html = str_replace('</body>', $text . '</body>', $html);
}
private static function injectHead(&$html, $text)
{
$html = str_replace('</head>', $text . '</head>', $html);
}
private static function minifyScripts(&$html)
{
$scriptsToMinify = [];
$html = preg_replace_callback(
'/<script[^>]*src="([^"]+)"[^>]*><\/script>/',
function($matches) use (&$scriptsToMinify)
{
$scriptPath = $matches[1];
if (substr($scriptPath, 0, 2) == '//' or strpos($scriptPath, 'http') !== false)
return $matches[0];
$scriptsToMinify []= __DIR__ . DS . $scriptPath;
return '';
}, $html);
$out = '<script type="text/javascript">';
foreach ($scriptsToMinify as $scriptPath)
$out .= Compressor::js(file_get_contents($scriptPath));
$out .= '</script>';
self::injectBody($html, $out);
}
private static function minifyStylesheets(&$html)
{
$html = preg_replace_callback(
'/<link[^>]*href="([^"]+)"[^>]*>/',
function($matches) use (&$stylesToMinify)
{
$stylePath = $matches[1];
if (substr($stylePath, 0, 2) == '//' or strpos($stylePath, 'http') !== false)
return $matches[0];
if (strpos($matches[0], 'css') === false)
return $matches[0];
$stylesToMinify []= __DIR__ . DS . $stylePath;
return '';
}, $html);
$out = '<style type="text/css">';
foreach ($stylesToMinify as $stylePath)
$out .= Compressor::css(file_get_contents($stylePath));
$out .= '</style>';
self::injectHead($html, $out);
}
private static function includeTemplates(&$html)
{
$templatesToInclude = [];
foreach (glob(__DIR__ . DS . 'templates' . DS . '*.tpl') as $templatePath)
$templatesToInclude []= $templatePath;
$out = '';
foreach ($templatesToInclude as $templatePath)
{
$out .= '<script type="text/template" id="' . str_replace('.tpl', '-template', basename($templatePath)) . '">';
$out .= Compressor::html(file_get_contents($templatePath));
$out .= '</script>';
}
self::injectBody($html, $out);
}
}
$compiledPath = __DIR__ . DS . 'index-compiled.html';
$html = IndexBuilder::build();
$html = Compressor::html($html);
file_put_contents($compiledPath, $html);

View file

@ -2,11 +2,20 @@
<html>
<head>
<meta charset="utf-8"/>
<title>szurubooru</title>
<!-- build:remove -->
<title>szuru2</title>
<!-- /build -->
<!-- build:template
<title><%= serviceName %></title>
/build -->
<script src="//cdnjs.cloudflare.com/ajax/libs/path.js/0.8.4/path.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>
<!-- build:template
<link rel="stylesheet" type="text/css" href="app.min.css?<%= timestamp %>"/>
/build -->
<!-- build:remove -->
<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Droid+Sans:400,700"/>
<link rel="stylesheet" type="text/css" href="/css/core.css"/>
<link rel="stylesheet" type="text/css" href="/css/forms.css"/>
@ -17,6 +26,7 @@
<link rel="stylesheet" type="text/css" href="/css/registration-form.css"/>
<link rel="stylesheet" type="text/css" href="/css/user-list.css"/>
<link rel="stylesheet" type="text/css" href="/css/user.css"/>
<!-- /build -->
</head>
<body>
@ -28,6 +38,18 @@
</div>
</div>
<!-- build:template
<% _.each(templates, function(templateString, templateName) { %>
<script type="text/template" id="<%= templateName %>-template">
<%= templateString %>
</script>
<% }) %>
/build -->
<!-- build:template
<script type="text/javascript" src="app.min.js?<%= timestamp %>"></script>
/build -->
<!-- build:remove -->
<script type="text/javascript" src="/js/DI.js"></script>
<script type="text/javascript" src="/js/Promise.js"></script>
<script type="text/javascript" src="/js/State.js"></script>
@ -56,5 +78,6 @@
<script type="text/javascript" src="/js/Presenters/UserActivationPresenter.js"></script>
<script type="text/javascript" src="/js/Router.js"></script>
<script type="text/javascript" src="/js/Bootstrap.js"></script>
<!-- /build -->
</body>
</html>