Improve compilation speed for development builds (#402)

* Improve incremental build times
* Live-reloading in development mode
This commit is contained in:
Ruin0x11 2021-05-14 07:39:40 -07:00 committed by GitHub
parent ca77149597
commit a6886ddb89
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 5631 additions and 179 deletions

View file

@ -4,20 +4,20 @@
// -------------------------------------------------
const webapp_icons = [
{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}
{ 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 }
];
const webapp_splash_screens = [
{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}
{ 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 }
];
const external_js = [
@ -57,6 +57,11 @@ const glob = require('glob');
const path = require('path');
const util = require('util');
const execSync = require('child_process').execSync;
const browserify = require('browserify');
const chokidar = require('chokidar');
const WebSocket = require('ws');
var PrettyError = require('pretty-error');
var pe = new PrettyError();
function readTextFile(path) {
return fs.readFileSync(path, 'utf-8');
@ -112,7 +117,7 @@ function bundleHtml() {
(match, number) => { return placeholders[number]; });
const functionText = underscore.template(
templateText, {variable: 'ctx'}).source;
templateText, { variable: 'ctx' }).source;
compiledTemplateJs.push(`templates['${name}'] = ${functionText};`);
}
@ -131,7 +136,7 @@ function bundleCss() {
let css = '';
for (const file of glob.sync('./css/**/*.styl')) {
css += stylus.render(readTextFile(file), {filename: file});
css += stylus.render(readTextFile(file), { filename: file });
}
fs.writeFileSync('./public/css/app.min.css', minifyCss(css));
if (process.argv.includes('--gzip')) {
@ -148,59 +153,69 @@ function bundleCss() {
console.info('Bundled CSS');
}
function minifyJs(path) {
return require('terser').minify(
fs.readFileSync(path, 'utf-8'), { compress: { unused: false } }).code;
}
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();
});
}
function bundleVendorJs(compress) {
let b = browserify();
for (let lib of external_js) {
b.require(lib);
}
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);
}
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);
}
console.info('Bundled app JS');
callback();
});
}
function bundleJs() {
const browserify = require('browserify');
function minifyJs(path) {
return require('terser').minify(
fs.readFileSync(path, 'utf-8'), {compress: {unused: false}}).code;
}
function writeJsBundle(b, path, compress, callback) {
let outputFile = fs.createWriteStream(path);
b.bundle().pipe(outputFile);
outputFile.on('finish', () => {
if (compress) {
fs.writeFileSync(path, minifyJs(path));
}
callback();
});
}
if (!process.argv.includes('--no-vendor-js')) {
let b = browserify();
for (let lib of external_js) {
b.require(lib);
}
if (!process.argv.includes('--no-transpile')) {
b.add(require.resolve('babel-polyfill'));
}
const file = './public/js/vendor.min.js';
writeJsBundle(b, file, true, () => {
if (process.argv.includes('--gzip')) {
gzipFile(file);
}
console.info('Bundled vendor JS');
});
bundleVendorJs(true);
}
if (!process.argv.includes('--no-app-js')) {
let b = browserify({debug: process.argv.includes('--debug')});
let watchify = require('watchify');
let b = browserify({ debug: process.argv.includes('--debug') });
if (!process.argv.includes('--no-transpile')) {
b = b.transform('babelify');
}
b = b.external(external_js).add(glob.sync('./js/**/*.js'));
const compress = !process.argv.includes('--debug');
const file = './public/js/app.min.js';
writeJsBundle(b, file, compress, () => {
if (process.argv.includes('--gzip')) {
gzipFile(file);
}
console.info('Bundled app JS');
});
bundleAppJs(b, compress, () => { });
}
}
const environment = process.argv.includes('--watch') ? "development" : "production";
function bundleConfig() {
function getVersion() {
let build_info = process.env.BUILD_INFO;
@ -216,9 +231,10 @@ function bundleConfig() {
}
const config = {
meta: {
version: getVersion(),
buildDate: new Date().toUTCString()
}
version: getVersion(),
buildDate: new Date().toUTCString()
},
environment: environment
};
fs.writeFileSync('./js/.config.autogen.json', JSON.stringify(config));
@ -255,31 +271,31 @@ function bundleWebAppFiles() {
Promise.all(webapp_icons.map(icon => {
return Jimp.read('./img/app.png')
.then(file => {
file.resize(icon.size, Jimp.AUTO, Jimp.RESIZE_BEZIER)
.write(path.join('./public/img/', icon.name));
});
.then(file => {
file.resize(icon.size, Jimp.AUTO, Jimp.RESIZE_BEZIER)
.write(path.join('./public/img/', icon.name));
});
}))
.then(() => {
console.info('Generated webapp icons');
});
.then(() => {
console.info('Generated webapp icons');
});
Promise.all(webapp_splash_screens.map(dim => {
return Jimp.read('./img/splash.png')
.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'));
});
.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'));
});
}))
.then(() => {
console.info('Generated splash screens');
});
.then(() => {
console.info('Generated splash screens');
});
}
function makeOutputDirs() {
@ -298,22 +314,111 @@ function makeOutputDirs() {
}
}
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));
}
});
bundleBinaryAssets();
bundleWebAppFiles();
bundleCss();
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();
}
// -------------------------------------------------
console.log("Building for '" + environment + "' environment.");
makeOutputDirs();
bundleConfig();
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();
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();
}
}

View file

@ -1,7 +1,7 @@
<ul>
<li><%- ctx.postCount %> posts</li><span class='sep'>
</span><li><%= ctx.makeFileSize(ctx.diskUsage) %></li><span class='sep'>
</span><li>Build <a class='version' href='https://github.com/rr-/szurubooru/commits/master'><%- ctx.version %></a> from <%= ctx.makeRelativeTime(ctx.buildDate) %></li><span class='sep'>
</span><li>Build <a class='version' href='https://github.com/rr-/szurubooru/commits/master'><%- ctx.version %></a><%- ctx.isDevelopmentMode ? " (DEV MODE)" : "" %> from <%= ctx.makeRelativeTime(ctx.buildDate) %></li><span class='sep'>
</span><% if (ctx.canListSnapshots) { %><li><a href='<%- ctx.formatClientLink('history') %>'>History</a></li><span class='sep'>
</span><% } %>
</ul>

View file

@ -17,6 +17,7 @@ class HomeController {
buildDate: config.meta.buildDate,
canListSnapshots: api.hasPrivilege("snapshots:list"),
canListPosts: api.hasPrivilege("posts:list"),
isDevelopmentMode: config.environment == "development"
});
Info.get().then(

View file

@ -1,5 +1,20 @@
"use strict";
const config = require("./config.js");
if (config.environment == "development") {
var ws = new WebSocket("ws://" + location.hostname + ":8080");
ws.addEventListener('open', function (event) {
console.log("Live-reloading websocket connected.");
});
ws.addEventListener('message', (event) => {
console.log(event);
if (event.data == 'reload'){
location.reload();
}
});
}
require("./util/polyfill.js");
const misc = require("./util/misc.js");
const views = require("./util/views.js");

5499
client/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,7 @@
"private": true,
"scripts": {
"build": "node build.js",
"watch": "c1=\"\";while :;do c2=$(find html js css img -type f -and -not -iname '*autogen*'|sort|xargs cat|md5sum);[[ $c1 != $c2 ]]&&npm run build -- --debug --no-vendor-js;c1=$c2;sleep 1;done"
"watch": "node build.js --watch"
},
"dependencies": {
"dompurify": "^2.0.17",
@ -21,12 +21,16 @@
"babel-preset-env": "^1.7.0",
"babelify": "^8.0.0",
"browserify": "^16.2.2",
"chokidar": "^3.5.1",
"csso": "^3.5.1",
"glob": "^7.1.2",
"html-minifier": "^3.5.18",
"jimp": "^0.13.0",
"pretty-error": "^3.0.3",
"stylus": "^0.54.5",
"terser": "^3.7.7",
"underscore": "^1.9.1"
"underscore": "^1.9.1",
"watchify": "^4.0.0",
"ws": "^7.4.5"
}
}