diff options
| author | Shyam Sunder <sgsunder1@gmail.com> | 2018-09-03 17:36:06 -0400 |
|---|---|---|
| committer | rr- <rr-@sakuya.pl> | 2018-12-22 12:31:25 +0100 |
| commit | a5a06bf2d10b2a2e16ae3e91cf119c56cb050532 (patch) | |
| tree | 6f8d261352e9d287125445155cf3de9cb0312a77 | |
| parent | e6445b431f9f155b4511d8ceeffaf3a3fded425b (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/Dockerfile | 7 | ||||
| -rwxr-xr-x[-rw-r--r--] | client/build.js | 357 | ||||
| -rw-r--r-- | client/css/core-general.styl | 2 | ||||
| -rw-r--r-- | client/nginx.conf.docker | 2 |
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; |