@ -26,31 +26,26 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// Dependencies
// Dependencies
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
const path = require ( 'path' ) ;
const gulp = require ( 'gulp' ) ;
const gulp = require ( 'gulp' ) ;
const through = require ( 'through2' ) ;
const through = require ( 'through2' ) ;
const htmlmin = require ( 'gulp-htmlmin' ) ;
const inline = require ( 'gulp-inline' ) ;
const inlineImages = require ( 'gulp-css-base64' ) ;
const favicon = require ( 'gulp-base64-favicon' ) ;
const crass = require ( 'gulp-crass' ) ;
const htmllint = require ( 'gulp-htmllint' ) ;
const htmllint = require ( 'gulp-htmllint' ) ;
const csslint = require ( 'gulp-csslint' ) ;
const csslint = require ( 'gulp-csslint' ) ;
const htmlmin = require ( 'html-minifier' ) ;
const gzip = require ( 'gulp-gzip' ) ;
const inline = require ( 'gulp-inline-source-html' ) ;
const rename = require ( 'gulp-rename' ) ;
const rename = require ( 'gulp-rename' ) ;
const replace = require ( 'gulp-replace' ) ;
const replace = require ( 'gulp-replace' ) ;
const remover = require ( 'gulp-remove-code' ) ;
const gzip = require ( 'gulp-gzip' ) ;
const path = require ( 'path' ) ;
const terser = require ( 'terser' )
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Configuration
// Configuration
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
const htmlFolder = 'html/' ;
const htmlFolder = 'html/' ;
const configFolder = 'espurna/config/' ;
const dataFolder = 'espurna/data/' ;
const dataFolder = 'espurna/data/' ;
const staticFolder = 'espurna/static/' ;
const staticFolder = 'espurna/static/' ;
@ -58,6 +53,19 @@ const staticFolder = 'espurna/static/';
// Methods
// Methods
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
var toMinifiedHtml = function ( options ) {
return through . obj ( function ( source , encoding , callback ) {
if ( source . isNull ( ) ) {
callback ( null , source ) ;
return ;
}
var contents = source . contents . toString ( ) ;
source . contents = Buffer . from ( htmlmin . minify ( contents , options ) ) ;
callback ( null , source ) ;
} ) ;
}
var toHeader = function ( name , debug ) {
var toHeader = function ( name , debug ) {
return through . obj ( function ( source , encoding , callback ) {
return through . obj ( function ( source , encoding , callback ) {
@ -108,32 +116,92 @@ var htmllintReporter = function(filepath, issues) {
}
}
} ;
} ;
var compressJs = function ( ) {
return through . obj ( function ( source , encoding , callback ) {
// TODO: this is a roughly equivalent port of the gulp-remove-code,
// which also uses regexp rules to filter in-between specially-formatted comment blocks
var jsRegexp = function ( module ) {
return '//\\s*removeIf\\(!' + module + '\\)'
+ '\\s*(\n|\r|.)*?'
+ '//\\s*endRemoveIf\\(!' + module + '\\)' ;
}
var cssRegexp = function ( module ) {
return '/\\*\\s*removeIf\\(!' + module + '\\)\\s*\\*/'
+ '\\s*(\n|\r|.)*?'
+ '/\\*\\s*endRemoveIf\\(!' + module + '\\)\\s*\\*/' ;
}
var htmlRegexp = function ( module ) {
return '<!--\\s*removeIf\\(!' + module + '\\)\\s*-->'
+ '\\s*(\n|\r|.)*?'
+ '<!--\\s*endRemoveIf\\(!' + module + '\\)\\s*-->' ;
}
var generateRegexps = function ( modules , func ) {
var regexps = new Set ( ) ;
for ( const [ module , enabled ] of Object . entries ( modules ) ) {
if ( enabled ) {
continue ;
}
const expression = func ( module ) ;
const re = new RegExp ( expression , 'gm' ) ;
regexps . add ( re ) ;
}
return regexps ;
}
// TODO: use html parser here?
// TODO: separate js files to include js, html & css and avoid 2 step regexp?
var htmlRemover = function ( modules ) {
const regexps = generateRegexps ( modules , htmlRegexp ) ;
return through . obj ( function ( source , _ , callback ) {
if ( source . isNull ( ) ) {
if ( source . isNull ( ) ) {
callback ( null , source ) ;
callback ( null , source ) ;
return ;
return ;
}
}
if ( source . path . endsWith ( "custom.js" ) ) {
var contents = source . contents . toString ( ) ;
var result = terser . minify ( contents ) ;
if ( result . error ) {
var { message , filename , line , col , pos } = result . error ;
throw new Error ( "terser error `" + message + "` in file " + filename + ", at line " + line ) ;
var contents = source . contents . toString ( ) ;
for ( var regexp of regexps ) {
contents = contents . replace ( regexp , '' ) ;
}
source . contents = Buffer . from ( contents ) ;
callback ( null , source ) ;
} ) ;
}
var inlineHandler = function ( modules ) {
return function ( source ) {
if ( ( ( source . sourcepath === 'custom.css' ) || ( source . sourcepath === 'custom.js' ) ) ) {
const filter = ( source . type === 'css' ) ? cssRegexp : jsRegexp ;
const regexps = generateRegexps ( modules , filter ) ;
var content = source . fileContent ;
for ( var regexp of regexps ) {
content = content . replace ( regexp , '' ) ;
}
}
source . contents = Buffer . from ( result . code ) ;
this . push ( source ) ;
callback ( ) ;
source . fileContent = content ;
return ;
}
if ( source . content ) {
return ;
return ;
}
}
callback ( null , source ) ;
return ;
} ) ;
} ;
// Just ignore the vendored libs, repackaging makes things worse for the size
const path = source . sourcepath ;
if ( path . endsWith ( '.min.js' ) ) {
source . compress = false ;
} else if ( path . endsWith ( '.min.css' ) ) {
source . compress = false ;
}
} ;
}
var buildWebUI = function ( module ) {
var buildWebUI = function ( module ) {
@ -178,22 +246,16 @@ var buildWebUI = function(module) {
'rules' : {
'rules' : {
'id-class-style' : false ,
'id-class-style' : false ,
'label-req-for' : false ,
'label-req-for' : false ,
'line-end-style' : false
'line-end-style' : false ,
'attr-req-value' : false
}
}
} , htmllintReporter ) ) .
} , htmllintReporter ) ) .
pipe ( remover ( modules ) ) .
pipe ( favicon ( ) ) .
pipe ( inline ( {
base : htmlFolder ,
js : [ function ( ) { return remover ( modules ) ; } , compressJs ] ,
css : [ crass , inlineImages ] ,
disabledTypes : [ 'svg' , 'img' ]
} ) ) .
pipe ( remover ( modules ) ) .
pipe ( htmlmin ( {
pipe ( htmlRemover ( modules ) ) .
pipe ( inline ( { handlers : [ inlineHandler ( modules ) ] } ) ) .
pipe ( toMinifiedHtml ( {
collapseWhitespace : true ,
collapseWhitespace : true ,
removeComments : true ,
removeComments : true ,
minifyCSS : tru e,
minifyCSS : false ,
minifyJS : false
minifyJS : false
} ) ) .
} ) ) .
pipe ( replace ( 'pure-' , 'p-' ) ) .
pipe ( replace ( 'pure-' , 'p-' ) ) .
@ -211,7 +273,7 @@ var buildWebUI = function(module) {
gulp . task ( 'certs' , function ( ) {
gulp . task ( 'certs' , function ( ) {
gulp . src ( dataFolder + 'server.*' ) .
gulp . src ( dataFolder + 'server.*' ) .
pipe ( toHeader ( debug = false ) ) .
pipe ( toHeader ( '' , false ) ) .
pipe ( gulp . dest ( staticFolder ) ) ;
pipe ( gulp . dest ( staticFolder ) ) ;
} ) ;
} ) ;