2023-11-06 11:00:02 +01:00
const fs = require ( 'fs' ) ;
const path = require ( "path" ) ;
const TOML = require ( 'fast-toml' ) ;
const UglifyJS = require ( 'uglify-js' ) ;
const jsonminify = require ( "jsonminify" ) ;
const replace = require ( 'replace-in-file' ) ;
const util = require ( "util" ) ;
const { exec } = require ( "child_process" ) ;
const execPromise = util . promisify ( exec ) ;
2024-03-22 09:36:26 +01:00
if ( ! ( fs . existsSync ( 'config.toml' ) ) ) {
throw new Error ( 'ERROR: cannot find config.toml!' ) ;
}
2023-11-06 11:00:02 +01:00
const tomlString = String ( fs . readFileSync ( 'config.toml' ) ) ;
const data = TOML . parse ( tomlString ) ;
const js _prestyle = data . extra . js _prestyle ;
const js _switcher = data . extra . js _switcher ;
const js _email _encode = data . extra . js _email _encode ;
const js _copycode = data . extra . js _copycode ;
const search _library = data . extra . search _library ;
const index _format = data . search . index _format ;
const uglyurls = data . extra . uglyurls ;
const js _bundle = data . extra . js _bundle ;
const offline = data . extra . offline ;
const online _url = data . extra . online _url ;
const online _indexformat = data . extra . online _indexformat ;
const pwa = data . extra . pwa ;
const pwa _VER = data . extra . pwa _VER ;
const pwa _NORM _TTL = data . extra . pwa _NORM _TTL ;
const pwa _LONG _TTL = data . extra . pwa _LONG _TTL ;
const pwa _TTL _NORM = data . extra . pwa _TTL _NORM ;
const pwa _TTL _LONG = data . extra . pwa _TTL _LONG ;
const pwa _TTL _EXEMPT = data . extra . pwa _TTL _EXEMPT ;
const pwa _cache _all = data . extra . pwa _cache _all ;
const pwa _BASE _CACHE _FILES = data . extra . pwa _BASE _CACHE _FILES ;
async function execWrapper ( cmd ) {
const { stdout , stderr } = await execPromise ( cmd ) ;
if ( stdout ) {
console . log ( stdout ) ;
}
if ( stderr ) {
2024-03-22 09:36:26 +01:00
console . log ( 'ERROR: ' + stderr ) ;
2023-11-06 11:00:02 +01:00
}
}
async function abridge ( ) {
if ( offline === false ) {
2024-03-22 09:36:26 +01:00
if ( typeof online _url !== 'undefined' && typeof online _indexformat !== 'undefined' ) {
replace . sync ( { files : 'config.toml' , from : /base_url.*=.*/g , to : "base_url = \"" + online _url + "\"" } ) ;
replace . sync ( { files : 'config.toml' , from : /index_format.*=.*/g , to : "index_format = \"" + online _indexformat + "\"" } ) ;
}
2023-11-06 11:00:02 +01:00
} else if ( offline === true ) {
2024-03-22 09:36:26 +01:00
if ( typeof online _url !== 'undefined' && typeof online _indexformat !== 'undefined' ) {
replace . sync ( { files : 'config.toml' , from : /base_url.*=.*/g , to : "base_url = \"" + _ _dirname + "\/public\"" } ) ;
replace . sync ( { files : 'config.toml' , from : /index_format.*=.*/g , to : "index_format = \"elasticlunr_javascript\"" } ) ;
} else {
throw new Error ( 'ERROR: offline = true requires that online_url and online_indexformat are set in config.toml, so that the base_url and index_format can be restored if offline is later set to false.' ) ;
}
2023-11-06 11:00:02 +01:00
}
console . log ( 'Zola Build to generate files for minification:' ) ;
await execWrapper ( 'zola build' ) ;
//check that static/js exists, do this after zola build, it will handle creating static if missing.
var jsdir = 'static/js' ;
try {
fs . mkdirSync ( jsdir ) ;
} catch ( e ) {
if ( e . code != 'EEXIST' ) throw e ;
}
// check if abridge is used directly or as a theme.
bpath = '' ;
if ( fs . existsSync ( './themes' ) ) {
bpath = 'themes/abridge/' ;
}
base _url = data . base _url ;
if ( base _url . slice ( - 1 ) == "/" ) {
base _url = base _url . slice ( 0 , - 1 ) ;
}
if ( search _library === 'elasticlunr' ) {
if ( fs . existsSync ( 'content/static/stork_toml.md' ) ) {
replace . sync ( { files : 'content/static/stork_toml.md' , from : /draft.*=.*/g , to : "draft = true" } ) ;
}
if ( fs . existsSync ( 'content/static/tinysearch_json.md' ) ) {
replace . sync ( { files : 'content/static/tinysearch_json.md' , from : /draft.*=.*/g , to : "draft = true" } ) ;
}
} else if ( search _library === 'tinysearch' ) {
if ( ! fs . existsSync ( 'content/static/tinysearch_json.md' ) ) { // 'content/static/tinysearch_json.md' file is missing, copy from abridge theme.
fs . copyFileSync ( bpath + 'content/static/tinysearch_json.md' , 'content/static/tinysearch_json.md' , fs . constants . COPYFILE _EXCL ) ;
}
if ( fs . existsSync ( 'content/static/stork_toml.md' ) ) {
replace . sync ( { files : 'content/static/stork_toml.md' , from : /draft.*=.*/g , to : "draft = true" } ) ;
}
if ( fs . existsSync ( 'content/static/tinysearch_json.md' ) ) {
replace . sync ( { files : 'content/static/tinysearch_json.md' , from : /draft.*=.*/g , to : "draft = false" } ) ;
}
// zola build && mkdir -p tmp && tinysearch --optimize --path tmp public/data_tinysearch/index.html && rsync -avz tmp/*.wasm static/ && rm -rf tmp
} else if ( search _library === 'stork' ) {
if ( ! fs . existsSync ( 'content/static/stork_toml.md' ) ) { // 'content/static/stork_toml.md' file is missing, copy from abridge theme.
fs . copyFileSync ( bpath + 'content/static/stork_toml.md' , 'content/static/stork_toml.md' , fs . constants . COPYFILE _EXCL ) ;
}
if ( fs . existsSync ( 'content/static/stork_toml.md' ) ) {
replace . sync ( { files : 'content/static/stork_toml.md' , from : /draft.*=.*/g , to : "draft = false" } ) ;
}
if ( fs . existsSync ( 'content/static/tinysearch_json.md' ) ) {
replace . sync ( { files : 'content/static/tinysearch_json.md' , from : /draft.*=.*/g , to : "draft = true" } ) ;
}
// zola build && stork build --input public/data_stork/index.html --output static/stork.st
}
if ( pwa ) { // Update pwa settings, file list, and hashes.
2024-03-22 09:36:26 +01:00
if ( typeof pwa _VER !== 'undefined' && typeof pwa _NORM _TTL !== 'undefined' && typeof pwa _LONG _TTL !== 'undefined' && typeof pwa _TTL _NORM !== 'undefined' && typeof pwa _TTL _LONG !== 'undefined' && typeof pwa _TTL _EXEMPT !== 'undefined' ) {
// update from abridge theme.
fs . copyFileSync ( bpath + 'static/sw.js' , 'static/sw.js' ) ;
fs . copyFileSync ( bpath + 'static/js/sw_load.js' , 'static/js/sw_load.js' ) ;
// Update settings in PWA javascript file, using options parsed from config.toml. sw.min.js?v=3.10.0", "++"
if ( fs . existsSync ( 'static/js/sw_load.js' ) ) {
sw _load _min = '.js?v=' ;
if ( js _bundle ) {
sw _load _min = '.min.js?v=' ;
}
replace . sync ( { files : 'static/js/sw_load.js' , from : /sw.*v=.*/g , to : "sw" + sw _load _min + pwa _VER + "\"," } ) ;
}
if ( fs . existsSync ( 'static/sw.js' ) ) {
replace . sync ( { files : 'static/sw.js' , from : /NORM_TTL.*=.*/g , to : "NORM_TTL = " + pwa _NORM _TTL + ";" } ) ;
replace . sync ( { files : 'static/sw.js' , from : /LONG_TTL.*=.*/g , to : "LONG_TTL = " + pwa _LONG _TTL + ";" } ) ;
replace . sync ( { files : 'static/sw.js' , from : /TTL_NORM.*=.*/g , to : "TTL_NORM = [" + pwa _TTL _NORM + "];" } ) ;
replace . sync ( { files : 'static/sw.js' , from : /TTL_LONG.*=.*/g , to : "TTL_LONG = [" + pwa _TTL _LONG + "];" } ) ;
replace . sync ( { files : 'static/sw.js' , from : /TTL_EXEMPT.*=.*/g , to : "TTL_EXEMPT = [" + pwa _TTL _EXEMPT + "];" } ) ;
2023-11-06 11:00:02 +01:00
}
2024-03-22 09:36:26 +01:00
if ( pwa _cache _all === true ) {
console . log ( 'info: pwa_cache_all = true in config.toml, so caching the entire site.\n' ) ;
// Generate array from the list of files, for the entire site.
2023-11-06 11:00:02 +01:00
2024-03-22 09:36:26 +01:00
var dir = 'public' ;
try {
fs . mkdirSync ( dir ) ;
} catch ( e ) {
if ( e . code != 'EEXIST' ) throw e ;
}
const path = './public/' ;
cache = 'this.BASE_CACHE_FILES = [' ;
files = fs . readdirSync ( path , { recursive : true , withFileTypes : false } )
. forEach (
( file ) => {
// check if is directory, if not then add the path/file
if ( ! fs . lstatSync ( path + file ) . isDirectory ( ) ) {
// format output
item = "/" + file . replace ( /index\.html$/i , '' ) ; // strip index.html from path
item = item . replace ( /^\/sw(\.min)?\.js/i , '' ) ; // dont cache service worker
2023-11-06 11:00:02 +01:00
2024-03-22 09:36:26 +01:00
// if formatted output is not empty line then append it to cache var
if ( item != '' ) { // skip empty lines
cache = cache + "'" + item + "'," ;
}
2023-11-06 11:00:02 +01:00
}
}
2024-03-22 09:36:26 +01:00
) ;
cache = cache . slice ( 0 , - 1 ) + '];' // remove the last comma and close the array
} else if ( pwa _BASE _CACHE _FILES ) {
cache = 'this.BASE_CACHE_FILES = [' + pwa _BASE _CACHE _FILES + '];' ;
}
2023-11-06 11:00:02 +01:00
2024-03-22 09:36:26 +01:00
// update the BASE_CACHE_FILES variable in the sw.js service worker file
results = replace . sync ( {
files : 'static/sw.js' ,
from : /this\.BASE_CACHE_FILES =.*/g ,
to : cache ,
countMatches : true ,
} ) ;
} else {
throw new Error ( 'ERROR: pwa requires that pwa_VER, pwa_NORM_TTL, pwa_LONG_TTL, pwa_TTL_NORM, pwa_TTL_LONG, pwa_TTL_EXEMPT are set in config.toml.' ) ;
}
2023-11-06 11:00:02 +01:00
}
if ( bpath === '' ) { // abridge used directly
// These are truely static js files, so they should only need to be updated by abridge maintainer or contributors.
minify ( [ 'static/js/theme.js' ] ) ;
minify ( [ 'static/js/theme_light.js' ] ) ;
minify ( [ 'static/js/katex.min.js' , 'static/js/mathtex-script-type.min.js' , 'static/js/katex-auto-render.min.js' , 'static/js/katexoptions.js' ] , 'static/js/katexbundle.min.js' ) ;
minify ( [ 'static/js/elasticlunr.min.js' , 'static/js/search.js' ] , 'static/js/search_elasticlunr.min.js' ) ;
minify ( [ 'static/js/stork.js' , 'static/js/stork_config.js' ] , 'static/js/search_stork.min.js' ) ;
minify ( [ 'static/js/tinysearch.js' ] , 'static/js/search_tinysearch.min.js' ) ;
minify ( [ 'static/js/prestyle.js' , 'static/js/theme_button.js' , 'static/js/email.js' , 'static/js/codecopy.js' , 'static/js/sw_load.js' ] , 'static/js/abridge_nosearch.min.js' ) ;
minify ( [ 'static/js/prestyle.js' , 'static/js/theme_button.js' , 'static/js/email.js' , 'static/js/codecopy.js' ] , 'static/js/abridge_nosearch_nopwa.min.js' ) ;
minify ( [ 'static/js/sw_load.js' ] ) ;
minify ( [ 'static/sw.js' ] ) ;
} else if ( pwa ) {
minify ( [ 'static/js/sw_load.js' ] ) ;
minify ( [ 'static/sw.js' ] ) ;
}
// if manifest.json is present, then minify it.
if ( fs . existsSync ( 'static/manifest.json' ) ) {
let out ;
try {
out = JSON . minify ( fs . readFileSync ( 'static/manifest.json' , { encoding : "utf-8" } ) ) ;
} catch ( err ) {
console . log ( err ) ;
}
fs . writeFileSync ( 'static/manifest.min.json' , out ) ;
}
abridge _bundle = bundle ( bpath , js _prestyle , js _switcher , js _email _encode , js _copycode , search _library , index _format , uglyurls , false ) ;
minify ( abridge _bundle , 'static/js/abridge_nopwa.min.js' ) ;
abridge _bundle = bundle ( bpath , js _prestyle , js _switcher , js _email _encode , js _copycode , search _library , index _format , uglyurls , pwa ) ;
minify ( abridge _bundle , 'static/js/abridge.min.js' ) ;
console . log ( 'Zola Build to generate new integrity hashes for the previously minified files:' ) ;
await execWrapper ( 'zola build' ) ;
}
function bundle ( bpath , js _prestyle , js _switcher , js _email _encode , js _copycode , search _library , index _format , uglyurls , pwa ) {
minify _files = [ ] ;
if ( js _prestyle ) {
minify _files . push ( bpath + 'static/js/prestyle.js' ) ;
}
if ( js _switcher ) {
minify _files . push ( bpath + 'static/js/theme_button.js' ) ;
}
if ( js _email _encode ) {
minify _files . push ( bpath + 'static/js/email.js' ) ;
}
if ( js _copycode ) {
minify _files . push ( bpath + 'static/js/codecopy.js' ) ;
}
if ( search _library ) {
if ( ( search _library === 'elasticlunr' && offline === true ) || ( search _library === 'elasticlunr' && index _format === 'elasticlunr_javascript' && uglyurls === true ) ) {
minify _files . push ( 'public/search_index.en.js' ) ;
minify _files . push ( bpath + 'static/js/elasticlunr.min.js' ) ;
minify _files . push ( bpath + 'static/js/searchjavaugly.js' ) ;
} else if ( search _library === 'elasticlunr' && index _format === 'elasticlunr_javascript' ) {
minify _files . push ( 'public/search_index.en.js' ) ;
minify _files . push ( bpath + 'static/js/elasticlunr.min.js' ) ;
minify _files . push ( bpath + 'static/js/searchjava.js' ) ;
} else if ( search _library === 'elasticlunr' ) { //abridge default
minify _files . push ( bpath + 'static/js/elasticlunr.min.js' ) ;
minify _files . push ( bpath + 'static/js/search.js' ) ;
} else if ( search _library === 'stork' ) {
minify _files . push ( bpath + 'static/js/stork.js' ) ;
minify _files . push ( bpath + 'static/js/stork_config.js' ) ;
} else if ( search _library === 'tinysearch' ) {
minify _files . push ( bpath + 'static/js/tinysearch.js' ) ;
}
}
if ( pwa ) {
minify _files . push ( 'static/js/sw_load.js' ) ;
}
return minify _files ;
}
function minify ( fileA , outfile ) {
const options = {
mangle : true ,
compress : {
//expression: true,//Parse a single expression, rather than a program (for parsing JSON).
//global_defs: false,// a way to pass parameters
//module: true,//Process input as ES module (implies --toplevel)
//toplevel: true,//Compress and/or mangle variables in top level scope.
hoist _funs : true , //hoist function declarations
unsafe : true ,
unsafe _comps : true ,
unsafe _Function : true ,
unsafe _math : true ,
unsafe _proto : true ,
unsafe _regexp : true ,
unsafe _undefined : true ,
drop _console : true
}
}
if ( ! outfile ) { // outfile parameter omitted, infer based on input
outfile = fileA [ 0 ] . slice ( 0 , - 2 ) + 'min.js' ;
}
var filesContents = fileA . map ( function ( file ) { // array input to support multiple files
return fs . readFileSync ( file , 'utf8' ) ;
} ) ;
result = UglifyJS . minify ( filesContents , options ) ;
fs . writeFileSync ( outfile , result . code ) ;
}
abridge ( ) ;