summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShyam Sunder <sgsunder1@gmail.com>2018-09-03 17:36:06 -0400
committerrr- <rr-@sakuya.pl>2018-12-22 12:31:25 +0100
commita5a06bf2d10b2a2e16ae3e91cf119c56cb050532 (patch)
tree6f8d261352e9d287125445155cf3de9cb0312a77
parente6445b431f9f155b4511d8ceeffaf3a3fded425b (diff)
client/build: Clean up build process
Fixes incorrect URIs of iOS splash screens and OpenSans font Files get gzipped inside build script Better nginx configuration build.js uses more consistent, synchronous code
-rw-r--r--client/Dockerfile7
-rwxr-xr-x[-rw-r--r--]client/build.js357
-rw-r--r--client/css/core-general.styl2
-rw-r--r--client/nginx.conf.docker2
4 files changed, 197 insertions, 171 deletions
diff --git a/client/Dockerfile b/client/Dockerfile
index 69555a4..a3d0ffc 100644
--- a/client/Dockerfile
+++ b/client/Dockerfile
@@ -8,10 +8,7 @@ COPY . ./
ARG BUILD_INFO="docker-latest"
ARG CLIENT_BUILD_ARGS=""
-RUN BASE_URL="__BASEURL__" node build.js ${CLIENT_BUILD_ARGS}
-
-RUN find public/ -name public/index.html -prune -o -type f -size +5k \
- -print0 | xargs -0 -- gzip -6 -k
+RUN BASE_URL="__BASEURL__" node build.js --gzip ${CLIENT_BUILD_ARGS}
FROM nginx:alpine
@@ -23,7 +20,7 @@ RUN \
echo 'sed -i "s|__BACKEND__|${BACKEND_HOST}|" /etc/nginx/nginx.conf' \
>> /init && \
echo 'sed -i "s|__BASEURL__|${BASE_URL:-/}|" /var/www/index.htm' >> /init && \
- echo 'exec nginx -g "daemon off;"' >> /init && \
+ echo 'exec nginx' >> /init && \
chmod a+x /init
CMD ["/init"]
diff --git a/client/build.js b/client/build.js
index fdc5fd0..dd63a5f 100644..100755
--- a/client/build.js
+++ b/client/build.js
@@ -1,5 +1,35 @@
+#!/usr/bin/env node
'use strict';
+// -------------------------------------------------
+
+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}
+];
+
+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}
+];
+
+const external_js = [
+ 'underscore',
+ 'superagent',
+ 'mousetrap',
+ 'js-cookie',
+ 'nprogress',
+];
+
+// -------------------------------------------------
+
const fs = require('fs');
const glob = require('glob');
const path = require('path');
@@ -10,227 +40,224 @@ function readTextFile(path) {
return fs.readFileSync(path, 'utf-8');
}
-function writeFile(path, content) {
- return fs.writeFileSync(path, content);
+function gzipFile(file) {
+ file = path.normalize(file);
+ execSync('gzip -6 -k ' + file);
}
-function getVersion() {
- let build_info = process.env.BUILD_INFO;
- if (build_info) {
- return build_info.trim();
- } else {
- try {
- build_info = execSync('git describe --always --dirty --long --tags')
- .toString();
- } catch (e) {
- console.warn('Cannot find build version');
- return 'unknown';
- }
- return build_info.trim();
- }
-}
+// -------------------------------------------------
-function getConfig() {
- let config = {
- meta: {
- version: getVersion(),
- buildDate: new Date().toUTCString()
- }
- };
+function bundleHtml() {
+ const underscore = require('underscore');
+ const babelify = require('babelify');
- return config;
-}
+ const baseUrl = process.env.BASE_URL ? process.env.BASE_URL : '/';
-function copyFile(source, target) {
- fs.createReadStream(source).pipe(fs.createWriteStream(target));
-}
+ function minifyHtml(html) {
+ return require('html-minifier').minify(html, {
+ removeComments: true,
+ collapseWhitespace: true,
+ conservativeCollapse: true,
+ }).trim();
+ }
-function minifyJs(path) {
- return require('terser').minify(fs.readFileSync(path, 'utf-8'), {compress: {unused: false}}).code;
-}
+ const baseHtml = readTextFile('./html/index.htm')
+ .replace('<!-- Base HTML Placeholder -->', `<base href="${baseUrl}"/>`);
+ fs.writeFileSync('./public/index.htm', minifyHtml(baseHtml));
-function minifyCss(css) {
- return require('csso').minify(css).css;
-}
+ let compiledTemplateJs = [
+ `'use strict';`,
+ `let _ = require('underscore');`,
+ `let templates = {};`
+ ];
-function minifyHtml(html) {
- return require('html-minifier').minify(html, {
- removeComments: true,
- collapseWhitespace: true,
- conservativeCollapse: true,
- }).trim();
-}
+ 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]; });
-function bundleHtml() {
- const underscore = require('underscore');
- const babelify = require('babelify');
- const baseHtml = readTextFile('./html/index.htm', 'utf-8');
- const baseUrl = process.env.BASE_URL ? process.env.BASE_URL : '/';
- const finalHtml = baseHtml.replace(
- '<!-- Base HTML Placeholder -->', `<base href="${baseUrl}"/>`);
- writeFile('./public/index.htm', minifyHtml(finalHtml));
+ const functionText = underscore.template(
+ templateText, {variable: 'ctx'}).source;
- glob('./html/**/*.tpl', {}, (er, files) => {
- let compiledTemplateJs = '\'use strict\'\n';
- compiledTemplateJs += 'let _ = require(\'underscore\');';
- compiledTemplateJs += 'let templates = {};';
- for (const file of files) {
- const name = path.basename(file, '.tpl').replace(/_/g, '-');
- const placeholders = [];
- let templateText = readTextFile(file, 'utf-8');
- 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]; });
+ compiledTemplateJs.push(`templates['${name}'] = ${functionText};`);
+ }
+ compiledTemplateJs.push('module.exports = templates;');
- const functionText = underscore.template(
- templateText, {variable: 'ctx'}).source;
- compiledTemplateJs += `templates['${name}'] = ${functionText};`;
- }
- compiledTemplateJs += 'module.exports = templates;';
- writeFile('./js/.templates.autogen.js', compiledTemplateJs);
- console.info('Bundled HTML');
- });
+ fs.writeFileSync('./js/.templates.autogen.js', compiledTemplateJs.join('\n'));
+ console.info('Bundled HTML');
}
function bundleCss() {
const stylus = require('stylus');
- glob('./css/**/*.styl', {}, (er, files) => {
- let css = '';
- for (const file of files) {
- css += stylus.render(
- readTextFile(file), {filename: file});
- }
- writeFile('./public/css/app.min.css', minifyCss(css));
- copyFile(
- './node_modules/font-awesome/css/font-awesome.min.css',
- './public/css/vendor.min.css');
+ function minifyCss(css) {
+ return require('csso').minify(css).css;
+ }
+
+ let css = '';
+ for (const file of glob.sync('./css/**/*.styl')) {
+ css += stylus.render(readTextFile(file), {filename: file});
+ }
+ fs.writeFileSync('./public/css/app.min.css', minifyCss(css));
+ if (process.argv.includes('--gzip')) {
+ gzipFile('./public/css/app.min.css');
+ }
- console.info('Bundled 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');
}
function bundleJs() {
const browserify = require('browserify');
- const external = [
- 'underscore',
- 'superagent',
- 'mousetrap',
- 'js-cookie',
- 'nprogress',
- ];
- function writeJsBundle(b, path, message, compress) {
+ 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', function() {
+ outputFile.on('finish', () => {
if (compress) {
- writeFile(path, minifyJs(path));
+ fs.writeFileSync(path, minifyJs(path));
}
- console.info(message);
+ callback();
});
}
- glob('./js/**/*.js', {}, (er, files) => {
- if (!process.argv.includes('--no-vendor-js')) {
- let b = browserify();
- for (let lib of external) {
- b.require(lib);
- }
- if (!process.argv.includes('--no-transpile')) {
- b.add(require.resolve('babel-polyfill'));
+ 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);
}
- writeJsBundle(
- b, './public/js/vendor.min.js', 'Bundled vendor JS', true);
+ console.info('Bundled vendor JS');
+ });
+ }
+
+ if (!process.argv.includes('--no-app-js')) {
+ 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');
+ });
+ }
+}
- if (!process.argv.includes('--no-app-js')) {
- let outputFile = fs.createWriteStream('./public/js/app.min.js');
- let b = browserify({debug: process.argv.includes('--debug')});
- if (!process.argv.includes('--no-transpile')) {
- b = b.transform('babelify');
+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';
}
- writeJsBundle(
- b.external(external).add(files),
- './public/js/app.min.js',
- 'Bundled app JS',
- !process.argv.includes('--debug'));
}
- });
+ return build_info.trim();
+ }
+ const config = {
+ meta: {
+ version: getVersion(),
+ buildDate: new Date().toUTCString()
+ }
+ };
+
+ fs.writeFileSync('./js/.config.autogen.json', JSON.stringify(config));
}
-function bundleConfig(config) {
- writeFile(
- './js/.config.autogen.json', JSON.stringify(config));
- glob('./node_modules/font-awesome/fonts/*.*', {}, (er, files) => {
- for (let file of files) {
- if (fs.lstatSync(file).isDirectory()) {
+function bundleBinaryAssets() {
+ fs.copyFileSync('./img/favicon.png', './public/img/favicon.png');
+ fs.copyFileSync('./img/transparency_grid.png', './public/img/transparency_grid.png');
+
+ 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')) {
continue;
}
- copyFile(file, path.join('./public/fonts/', path.basename(file)));
+ gzipFile(file);
}
- });
+ }
+ console.info('Copied Fonts')
}
-function bundleBinaryAssets() {
- copyFile('./img/favicon.png', './public/img/favicon.png');
- copyFile('./img/transparency_grid.png', './public/img/transparency_grid.png');
-
+function bundleWebAppFiles() {
const Jimp = require('jimp');
- for (let icon of [
- {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}
- ]) {
- Jimp.read('./img/app.png', (err, infile) => {
- infile
- .resize(icon.size, Jimp.AUTO, Jimp.RESIZE_BEZIER)
+ fs.copyFileSync('./app/manifest.json', './public/manifest.json');
+
+ 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));
});
- }
- console.info('Generated webapp icons');
+ }))
+ .then(() => {
+ console.info('Generated webapp icons');
+ });
- for (let dim of [
- {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}
- ]) {
- Jimp.read('./img/splash.png', (err, infile) => {
- infile
- .resize(dim.center, Jimp.AUTO, Jimp.RESIZE_BEZIER)
+ 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 + '-' + dim.h + '.png'));
+ 'apple-touch-startup-image-' + dim.w + 'x' + dim.h + '.png'));
});
- }
- console.info('Generated splash screens');
+ }))
+ .then(() => {
+ console.info('Generated splash screens');
+ });
}
-function bundleWebAppFiles() {
- copyFile('./app/manifest.json', './public/manifest.json');
-}
+// -------------------------------------------------
-const config = getConfig();
-bundleConfig(config);
+bundleConfig();
bundleBinaryAssets();
bundleWebAppFiles();
if (!process.argv.includes('--no-html')) {
diff --git a/client/css/core-general.styl b/client/css/core-general.styl
index f6d1382..df30782 100644
--- a/client/css/core-general.styl
+++ b/client/css/core-general.styl
@@ -6,7 +6,7 @@
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
- src: local('Open Sans'), local('OpenSans'), url(fonts/open_sans.woff2) format('woff2');
+ src: local('Open Sans'), local('OpenSans'), url(../fonts/open_sans.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
/* make <body> cover entire viewport */
diff --git a/client/nginx.conf.docker b/client/nginx.conf.docker
index 6af4095..c9d77ea 100644
--- a/client/nginx.conf.docker
+++ b/client/nginx.conf.docker
@@ -1,4 +1,5 @@
worker_processes 1;
+user nginx;
error_log /dev/stderr warn;
pid /var/run/nginx.pid;
@@ -54,3 +55,4 @@ http {
}
}
}
+daemon off;