2018-09-03 23:36:06 +02:00
|
|
|
#!/usr/bin/env node
|
2016-03-27 20:29:47 +02:00
|
|
|
'use strict';
|
|
|
|
|
2018-09-03 23:36:06 +02:00
|
|
|
// -------------------------------------------------
|
|
|
|
|
|
|
|
const webapp_icons = [
|
2021-05-14 16:39:40 +02:00
|
|
|
{ name: 'android-chrome-192x192.png', size: 192 },
|
|
|
|
{ name: 'android-chrome-512x512.png', size: 512 },
|
|
|
|
{ name: 'apple-touch-icon.png', size: 180 },
|
|
|
|
{ name: 'mstile-150x150.png', size: 150 }
|
2018-09-03 23:36:06 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
const webapp_splash_screens = [
|
2021-05-14 16:39:40 +02:00
|
|
|
{ w: 640, h: 1136, center: 320 },
|
|
|
|
{ w: 750, h: 1294, center: 375 },
|
|
|
|
{ w: 1125, h: 2436, center: 565 },
|
|
|
|
{ w: 1242, h: 2148, center: 625 },
|
|
|
|
{ w: 1536, h: 2048, center: 770 },
|
|
|
|
{ w: 1668, h: 2224, center: 820 },
|
|
|
|
{ w: 2048, h: 2732, center: 1024 }
|
2018-09-03 23:36:06 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
const external_js = [
|
2020-06-23 19:24:59 +02:00
|
|
|
'dompurify',
|
2018-09-03 23:36:06 +02:00
|
|
|
'js-cookie',
|
2020-06-23 18:58:44 +02:00
|
|
|
'marked',
|
|
|
|
'mousetrap',
|
2018-09-03 23:36:06 +02:00
|
|
|
'nprogress',
|
2020-06-23 18:58:44 +02:00
|
|
|
'superagent',
|
|
|
|
'underscore',
|
2018-09-03 23:36:06 +02:00
|
|
|
];
|
|
|
|
|
2018-09-05 03:55:59 +02:00
|
|
|
const app_manifest = {
|
|
|
|
name: 'szurubooru',
|
|
|
|
icons: [
|
|
|
|
{
|
|
|
|
src: baseUrl() + 'img/android-chrome-192x192.png',
|
|
|
|
type: 'image/png',
|
|
|
|
sizes: '192x192'
|
2020-05-04 23:44:16 +02:00
|
|
|
},
|
2018-09-05 03:55:59 +02:00
|
|
|
{
|
|
|
|
src: baseUrl() + 'img/android-chrome-512x512.png',
|
|
|
|
type: 'image/png',
|
|
|
|
sizes: '512x512'
|
|
|
|
}
|
|
|
|
],
|
|
|
|
start_url: baseUrl(),
|
|
|
|
theme_color: '#24aadd',
|
|
|
|
background_color: '#ffffff',
|
|
|
|
display: 'standalone'
|
|
|
|
}
|
|
|
|
|
2018-09-03 23:36:06 +02:00
|
|
|
// -------------------------------------------------
|
|
|
|
|
2016-03-27 20:29:47 +02:00
|
|
|
const fs = require('fs');
|
2016-03-27 21:10:41 +02:00
|
|
|
const glob = require('glob');
|
|
|
|
const path = require('path');
|
|
|
|
const util = require('util');
|
2016-03-31 23:18:08 +02:00
|
|
|
const execSync = require('child_process').execSync;
|
2021-05-14 16:39:40 +02:00
|
|
|
const browserify = require('browserify');
|
|
|
|
const chokidar = require('chokidar');
|
|
|
|
const WebSocket = require('ws');
|
|
|
|
var PrettyError = require('pretty-error');
|
|
|
|
var pe = new PrettyError();
|
2016-03-31 23:18:08 +02:00
|
|
|
|
2016-05-21 12:33:02 +02:00
|
|
|
function readTextFile(path) {
|
|
|
|
return fs.readFileSync(path, 'utf-8');
|
|
|
|
}
|
|
|
|
|
2018-09-03 23:36:06 +02:00
|
|
|
function gzipFile(file) {
|
|
|
|
file = path.normalize(file);
|
|
|
|
execSync('gzip -6 -k ' + file);
|
2016-05-21 12:33:02 +02:00
|
|
|
}
|
|
|
|
|
2018-09-05 03:55:59 +02:00
|
|
|
function baseUrl() {
|
|
|
|
return process.env.BASE_URL ? process.env.BASE_URL : '/';
|
|
|
|
}
|
|
|
|
|
2018-09-03 23:36:06 +02:00
|
|
|
// -------------------------------------------------
|
2016-03-27 20:29:47 +02:00
|
|
|
|
2018-09-03 23:36:06 +02:00
|
|
|
function bundleHtml() {
|
|
|
|
const underscore = require('underscore');
|
|
|
|
const babelify = require('babelify');
|
2016-03-27 20:29:47 +02:00
|
|
|
|
2018-09-03 23:36:06 +02:00
|
|
|
function minifyHtml(html) {
|
|
|
|
return require('html-minifier').minify(html, {
|
|
|
|
removeComments: true,
|
|
|
|
collapseWhitespace: true,
|
|
|
|
conservativeCollapse: true,
|
|
|
|
}).trim();
|
|
|
|
}
|
2016-05-21 12:33:02 +02:00
|
|
|
|
2018-09-03 23:36:06 +02:00
|
|
|
const baseHtml = readTextFile('./html/index.htm')
|
2018-09-05 03:55:59 +02:00
|
|
|
.replace('<!-- Base HTML Placeholder -->', `<base href="${baseUrl()}"/>`);
|
2018-09-03 23:36:06 +02:00
|
|
|
fs.writeFileSync('./public/index.htm', minifyHtml(baseHtml));
|
2016-05-21 09:46:41 +02:00
|
|
|
|
2018-09-03 23:36:06 +02:00
|
|
|
let compiledTemplateJs = [
|
|
|
|
`'use strict';`,
|
|
|
|
`let _ = require('underscore');`,
|
|
|
|
`let templates = {};`
|
|
|
|
];
|
2016-05-21 09:46:41 +02:00
|
|
|
|
2018-09-03 23:36:06 +02:00
|
|
|
for (const file of glob.sync('./html/**/*.tpl')) {
|
|
|
|
const name = path.basename(file, '.tpl').replace(/_/g, '-');
|
|
|
|
const placeholders = [];
|
|
|
|
let templateText = readTextFile(file);
|
|
|
|
templateText = templateText.replace(
|
|
|
|
/<%.*?%>/ig,
|
|
|
|
(match) => {
|
|
|
|
const ret = '%%%TEMPLATE' + placeholders.length;
|
|
|
|
placeholders.push(match);
|
|
|
|
return ret;
|
|
|
|
});
|
|
|
|
templateText = minifyHtml(templateText);
|
|
|
|
templateText = templateText.replace(
|
|
|
|
/%%%TEMPLATE(\d+)/g,
|
|
|
|
(match, number) => { return placeholders[number]; });
|
2016-05-21 09:46:41 +02:00
|
|
|
|
2018-09-03 23:36:06 +02:00
|
|
|
const functionText = underscore.template(
|
2021-05-14 16:39:40 +02:00
|
|
|
templateText, { variable: 'ctx' }).source;
|
2018-09-03 23:36:06 +02:00
|
|
|
|
|
|
|
compiledTemplateJs.push(`templates['${name}'] = ${functionText};`);
|
|
|
|
}
|
|
|
|
compiledTemplateJs.push('module.exports = templates;');
|
|
|
|
|
|
|
|
fs.writeFileSync('./js/.templates.autogen.js', compiledTemplateJs.join('\n'));
|
|
|
|
console.info('Bundled HTML');
|
2016-03-27 20:29:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function bundleCss() {
|
2016-04-13 20:06:34 +02:00
|
|
|
const stylus = require('stylus');
|
2016-05-21 12:33:02 +02:00
|
|
|
|
2018-09-03 23:36:06 +02:00
|
|
|
function minifyCss(css) {
|
|
|
|
return require('csso').minify(css).css;
|
|
|
|
}
|
2016-05-21 12:35:41 +02:00
|
|
|
|
2018-09-03 23:36:06 +02:00
|
|
|
let css = '';
|
|
|
|
for (const file of glob.sync('./css/**/*.styl')) {
|
2021-05-14 16:39:40 +02:00
|
|
|
css += stylus.render(readTextFile(file), { filename: file });
|
2018-09-03 23:36:06 +02:00
|
|
|
}
|
|
|
|
fs.writeFileSync('./public/css/app.min.css', minifyCss(css));
|
|
|
|
if (process.argv.includes('--gzip')) {
|
|
|
|
gzipFile('./public/css/app.min.css');
|
|
|
|
}
|
|
|
|
|
|
|
|
fs.copyFileSync(
|
|
|
|
'./node_modules/font-awesome/css/font-awesome.min.css',
|
|
|
|
'./public/css/vendor.min.css');
|
|
|
|
if (process.argv.includes('--gzip')) {
|
|
|
|
gzipFile('./public/css/vendor.min.css');
|
|
|
|
}
|
|
|
|
|
|
|
|
console.info('Bundled CSS');
|
2016-03-27 20:29:47 +02:00
|
|
|
}
|
|
|
|
|
2021-05-14 16:39:40 +02:00
|
|
|
function minifyJs(path) {
|
|
|
|
return require('terser').minify(
|
|
|
|
fs.readFileSync(path, 'utf-8'), { compress: { unused: false } }).code;
|
|
|
|
}
|
2016-05-21 09:46:41 +02:00
|
|
|
|
2021-05-14 16:39:40 +02:00
|
|
|
function writeJsBundle(b, path, compress, callback) {
|
|
|
|
let outputFile = fs.createWriteStream(path);
|
|
|
|
b.bundle().on('error', (e) => console.error(pe.render(e))).pipe(outputFile);
|
|
|
|
outputFile.on('finish', () => {
|
|
|
|
if (compress) {
|
|
|
|
fs.writeFileSync(path, minifyJs(path));
|
|
|
|
}
|
|
|
|
callback();
|
|
|
|
});
|
|
|
|
}
|
2018-09-03 23:36:06 +02:00
|
|
|
|
2021-05-14 16:39:40 +02:00
|
|
|
function bundleVendorJs(compress) {
|
|
|
|
let b = browserify();
|
|
|
|
for (let lib of external_js) {
|
|
|
|
b.require(lib);
|
2016-05-21 09:46:41 +02:00
|
|
|
}
|
2021-05-14 16:39:40 +02:00
|
|
|
if (!process.argv.includes('--no-transpile')) {
|
|
|
|
b.add(require.resolve('babel-polyfill'));
|
|
|
|
}
|
|
|
|
const file = './public/js/vendor.min.js';
|
|
|
|
writeJsBundle(b, file, compress, () => {
|
|
|
|
if (process.argv.includes('--gzip')) {
|
|
|
|
gzipFile(file);
|
2018-09-03 23:36:06 +02:00
|
|
|
}
|
2021-05-14 16:39:40 +02:00
|
|
|
console.info('Bundled vendor JS');
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function bundleAppJs(b, compress, callback) {
|
|
|
|
const file = './public/js/app.min.js';
|
|
|
|
writeJsBundle(b, file, compress, () => {
|
|
|
|
if (process.argv.includes('--gzip')) {
|
|
|
|
gzipFile(file);
|
2018-09-03 23:36:06 +02:00
|
|
|
}
|
2021-05-14 16:39:40 +02:00
|
|
|
console.info('Bundled app JS');
|
|
|
|
callback();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function bundleJs() {
|
|
|
|
if (!process.argv.includes('--no-vendor-js')) {
|
|
|
|
bundleVendorJs(true);
|
2018-09-03 23:36:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!process.argv.includes('--no-app-js')) {
|
2021-05-14 16:39:40 +02:00
|
|
|
let watchify = require('watchify');
|
|
|
|
let b = browserify({ debug: process.argv.includes('--debug') });
|
2018-09-03 23:36:06 +02:00
|
|
|
if (!process.argv.includes('--no-transpile')) {
|
|
|
|
b = b.transform('babelify');
|
2016-04-13 19:02:21 +02:00
|
|
|
}
|
2018-09-03 23:36:06 +02:00
|
|
|
b = b.external(external_js).add(glob.sync('./js/**/*.js'));
|
|
|
|
const compress = !process.argv.includes('--debug');
|
2021-05-14 16:39:40 +02:00
|
|
|
bundleAppJs(b, compress, () => { });
|
2018-09-03 23:36:06 +02:00
|
|
|
}
|
|
|
|
}
|
2016-05-14 13:50:39 +02:00
|
|
|
|
2021-05-14 16:39:40 +02:00
|
|
|
const environment = process.argv.includes('--watch') ? "development" : "production";
|
|
|
|
|
2018-09-03 23:36:06 +02:00
|
|
|
function bundleConfig() {
|
|
|
|
function getVersion() {
|
|
|
|
let build_info = process.env.BUILD_INFO;
|
|
|
|
if (!build_info) {
|
|
|
|
try {
|
|
|
|
build_info = execSync('git describe --always --dirty --long --tags').toString();
|
|
|
|
} catch (e) {
|
|
|
|
console.warn('Cannot find build version');
|
|
|
|
build_info = 'unknown';
|
2016-04-07 18:51:29 +02:00
|
|
|
}
|
2016-05-14 13:50:39 +02:00
|
|
|
}
|
2018-09-03 23:36:06 +02:00
|
|
|
return build_info.trim();
|
|
|
|
}
|
|
|
|
const config = {
|
|
|
|
meta: {
|
2021-05-14 16:39:40 +02:00
|
|
|
version: getVersion(),
|
|
|
|
buildDate: new Date().toUTCString()
|
|
|
|
},
|
|
|
|
environment: environment
|
2018-09-03 23:36:06 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
fs.writeFileSync('./js/.config.autogen.json', JSON.stringify(config));
|
2018-09-05 03:55:59 +02:00
|
|
|
console.info('Generated config file');
|
2016-03-27 20:29:47 +02:00
|
|
|
}
|
|
|
|
|
2018-09-03 23:36:06 +02:00
|
|
|
function bundleBinaryAssets() {
|
|
|
|
fs.copyFileSync('./img/favicon.png', './public/img/favicon.png');
|
2018-09-05 03:55:59 +02:00
|
|
|
console.info('Copied images');
|
2018-09-03 23:36:06 +02:00
|
|
|
|
2018-09-05 03:10:51 +02:00
|
|
|
fs.copyFileSync('./fonts/open_sans.woff2', './public/fonts/open_sans.woff2')
|
2018-09-03 23:36:06 +02:00
|
|
|
for (let file of glob.sync('./node_modules/font-awesome/fonts/*.*')) {
|
|
|
|
if (fs.lstatSync(file).isDirectory()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
fs.copyFileSync(file, path.join('./public/fonts/', path.basename(file)));
|
|
|
|
}
|
|
|
|
if (process.argv.includes('--gzip')) {
|
|
|
|
for (let file of glob.sync('./public/fonts/*.*')) {
|
|
|
|
if (file.endsWith('woff2')) {
|
2016-05-21 12:35:41 +02:00
|
|
|
continue;
|
|
|
|
}
|
2018-09-03 23:36:06 +02:00
|
|
|
gzipFile(file);
|
2016-05-21 12:35:41 +02:00
|
|
|
}
|
2018-09-03 23:36:06 +02:00
|
|
|
}
|
2018-09-05 03:55:59 +02:00
|
|
|
console.info('Copied fonts')
|
2016-03-27 20:29:47 +02:00
|
|
|
}
|
|
|
|
|
2018-09-03 23:36:06 +02:00
|
|
|
function bundleWebAppFiles() {
|
2018-08-23 19:10:51 +02:00
|
|
|
const Jimp = require('jimp');
|
|
|
|
|
2018-09-05 03:55:59 +02:00
|
|
|
fs.writeFileSync('./public/manifest.json', JSON.stringify(app_manifest));
|
|
|
|
console.info('Generated app manifest');
|
2018-09-03 23:36:06 +02:00
|
|
|
|
|
|
|
Promise.all(webapp_icons.map(icon => {
|
|
|
|
return Jimp.read('./img/app.png')
|
2021-05-14 16:39:40 +02:00
|
|
|
.then(file => {
|
|
|
|
file.resize(icon.size, Jimp.AUTO, Jimp.RESIZE_BEZIER)
|
|
|
|
.write(path.join('./public/img/', icon.name));
|
|
|
|
});
|
2018-09-03 23:36:06 +02:00
|
|
|
}))
|
2021-05-14 16:39:40 +02:00
|
|
|
.then(() => {
|
|
|
|
console.info('Generated webapp icons');
|
|
|
|
});
|
2018-09-03 23:36:06 +02:00
|
|
|
|
|
|
|
Promise.all(webapp_splash_screens.map(dim => {
|
|
|
|
return Jimp.read('./img/splash.png')
|
2021-05-14 16:39:40 +02:00
|
|
|
.then(file => {
|
|
|
|
file.resize(dim.center, Jimp.AUTO, Jimp.RESIZE_BEZIER)
|
|
|
|
.background(0xFFFFFFFF)
|
|
|
|
.contain(dim.w, dim.center,
|
|
|
|
Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_MIDDLE)
|
|
|
|
.contain(dim.w, dim.h,
|
|
|
|
Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_MIDDLE)
|
|
|
|
.write(path.join('./public/img/',
|
|
|
|
'apple-touch-startup-image-' + dim.w + 'x' + dim.h + '.png'));
|
|
|
|
});
|
2018-09-03 23:36:06 +02:00
|
|
|
}))
|
2021-05-14 16:39:40 +02:00
|
|
|
.then(() => {
|
|
|
|
console.info('Generated splash screens');
|
|
|
|
});
|
2016-03-30 16:06:51 +02:00
|
|
|
}
|
|
|
|
|
2018-09-05 03:10:51 +02:00
|
|
|
function makeOutputDirs() {
|
|
|
|
const dirs = [
|
|
|
|
'./public',
|
|
|
|
'./public/css',
|
|
|
|
'./public/fonts',
|
|
|
|
'./public/img',
|
|
|
|
'./public/js'
|
|
|
|
];
|
|
|
|
for (let dir of dirs) {
|
|
|
|
if (!fs.existsSync(dir)) {
|
|
|
|
fs.mkdirSync(dir, 0o755);
|
2018-09-05 03:55:59 +02:00
|
|
|
console.info('Created directory: ' + dir);
|
2018-09-05 03:10:51 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-14 16:39:40 +02:00
|
|
|
function watch() {
|
|
|
|
let wss = new WebSocket.Server({ port: 8080 });
|
|
|
|
const liveReload = !process.argv.includes('--no-live-reload');
|
|
|
|
|
|
|
|
function emitReload() {
|
|
|
|
if (liveReload) {
|
|
|
|
console.log("Requesting live reload.")
|
|
|
|
wss.clients.forEach((client) => {
|
|
|
|
if (client.readyState === WebSocket.OPEN) {
|
|
|
|
client.send("reload");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
chokidar.watch('./fonts/**/*').on('change', () => {
|
|
|
|
try {
|
|
|
|
bundleBinaryAssets();
|
|
|
|
emitReload();
|
|
|
|
} catch (e) {
|
|
|
|
console.error(pe.render(e));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
chokidar.watch('./img/**/*').on('change', () => {
|
|
|
|
try {
|
|
|
|
bundleWebAppFiles();
|
|
|
|
emitReload();
|
|
|
|
} catch (e) {
|
|
|
|
console.error(pe.render(e));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
chokidar.watch('./html/**/*.tpl').on('change', () => {
|
|
|
|
try {
|
|
|
|
bundleHtml();
|
|
|
|
} catch (e) {
|
|
|
|
console.error(pe.render(e));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
chokidar.watch('./css/**/*.styl').on('change', () => {
|
|
|
|
try {
|
|
|
|
bundleCss()
|
|
|
|
emitReload();
|
|
|
|
} catch (e) {
|
|
|
|
console.error(pe.render(e));
|
|
|
|
}
|
|
|
|
});
|
2018-08-05 02:58:59 +02:00
|
|
|
|
2020-05-04 23:44:16 +02:00
|
|
|
bundleBinaryAssets();
|
|
|
|
bundleWebAppFiles();
|
2016-05-14 14:47:48 +02:00
|
|
|
bundleCss();
|
2021-05-14 16:39:40 +02:00
|
|
|
bundleHtml();
|
|
|
|
|
|
|
|
bundleVendorJs(true);
|
|
|
|
|
|
|
|
let watchify = require('watchify');
|
|
|
|
let b = browserify({
|
|
|
|
debug: process.argv.includes('--debug'),
|
|
|
|
entries: ['js/main.js'],
|
|
|
|
cache: {},
|
|
|
|
packageCache: {},
|
|
|
|
});
|
|
|
|
|
|
|
|
b.plugin(watchify);
|
|
|
|
|
|
|
|
if (!process.argv.includes('--no-transpile')) {
|
|
|
|
b = b.transform('babelify');
|
|
|
|
}
|
|
|
|
b = b.external(external_js).add(glob.sync('./js/**/*.js'));
|
|
|
|
const compress = false;
|
|
|
|
|
|
|
|
function bundle(id) {
|
|
|
|
console.info("Rebundling app JS...");
|
|
|
|
let start = new Date();
|
|
|
|
bundleAppJs(b, compress, () => {
|
|
|
|
let end = new Date() - start;
|
|
|
|
console.info('Rebundled in %ds.', end / 1000)
|
|
|
|
emitReload();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
b.on('update', bundle);
|
|
|
|
bundle();
|
2016-05-14 14:47:48 +02:00
|
|
|
}
|
2021-05-14 16:39:40 +02:00
|
|
|
|
|
|
|
// -------------------------------------------------
|
|
|
|
|
|
|
|
console.log("Building for '" + environment + "' environment.");
|
|
|
|
makeOutputDirs();
|
|
|
|
bundleConfig();
|
|
|
|
if (process.argv.includes('--watch')) {
|
|
|
|
watch();
|
|
|
|
} else {
|
|
|
|
if (!process.argv.includes('--no-binary-assets')) {
|
|
|
|
bundleBinaryAssets();
|
|
|
|
}
|
|
|
|
if (!process.argv.includes('--no-web-app-files')) {
|
|
|
|
bundleWebAppFiles();
|
|
|
|
}
|
|
|
|
if (!process.argv.includes('--no-html')) {
|
|
|
|
bundleHtml();
|
|
|
|
}
|
|
|
|
if (!process.argv.includes('--no-css')) {
|
|
|
|
bundleCss();
|
|
|
|
}
|
|
|
|
if (!process.argv.includes('--no-js')) {
|
|
|
|
bundleJs();
|
|
|
|
}
|
2016-05-14 14:47:48 +02:00
|
|
|
}
|