Skip to content

Commit

Permalink
Precompress and optimize static files during build and serve them to …
Browse files Browse the repository at this point in the history
…the browsers.

- optimize png images with pngquant (quality 95) and advpng
- optimize svg images with svgo
- create precompressed variants of js, css, svg files: gz using zopfli and br using brotli

Currently using forked versions of expressjs/send/negotiator while waiting for the PRs to go through:
- jshttp/negotiator#49
- pillarjs/send#108
  • Loading branch information
Mikko Tiihonen authored and Mikko Tiihonen committed Nov 23, 2016
1 parent aeb1502 commit 737e59d
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 5 deletions.
24 changes: 24 additions & 0 deletions Dockerfile
Expand Up @@ -27,12 +27,36 @@ ADD . ${WORK}
# Build and set permissions for arbitary non-root users in OpenShift
# (https://docs.openshift.org/latest/creating_images/guidelines.html#openshift-specific-guidelines)
RUN \
echo "deb http://ftp.de.debian.org/debian/ stretch main contrib non-free\ndeb-src http://ftp.de.debian.org/debian/ stretch main contrib non-free" | tee /etc/apt/sources.list.d/stretch.list && \
apt-get update && \
apt-get install -y --no-install-recommends \
brotli \
zopfli \
advancecomp \
pngquant && \
npm install && \
npm rebuild node-sass && \
npm run build && \
rm -rf static docs test /tmp/* && \
npm prune --production && \
npm cache clean && \
apt-get purge --auto-remove -y \
brotli \
zopfli \
advancecomp \
pngquant \
# also remove packages that native node module compilations required \
g++ \
cpp \
gcc \
make \
autoconf \
automake \
libtool \
lib*-dev && \
apt-get autoremove -y && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* && \
chmod -R a+rwX . && \
chown -R 9999:9999 ${WORK}

Expand Down
11 changes: 7 additions & 4 deletions package.json
Expand Up @@ -23,10 +23,13 @@
"test-visual-update": "gemini update test/visual/",
"es-lint": "eslint .",
"lint": "npm run es-lint",
"build": "NODE_ENV=production webpack --progress --color --display-error-details",
"win-build": "set NODE_ENV=production&& webpack --progress --color",
"build": "npm run webpack && npm run precompress",
"win-build": "npm run win-webpack",
"webpack": "NODE_ENV=production webpack --progress --color --display-error-details",
"win-webpack": "set NODE_ENV=production&& webpack --progress --color",
"start": "NODE_ENV=production node --harmony server/server",
"win-start": "set NODE_ENV=production&& set CONFIG=hsl&& node --harmony server/server.js"
"win-start": "set NODE_ENV=production&& set CONFIG=hsl&& node --harmony server/server.js",
"precompress": "./precompress.sh _static"
},
"main": "server.js",
"repository": {
Expand Down Expand Up @@ -59,7 +62,7 @@
"csscolorparser": "1.0.3",
"debug": "2.3.3",
"element-resize-detector": "1.1.9",
"express": "4.14.0",
"express": "gmokki/express#6eeb054",
"fluxible": "1.2.0",
"fluxible-action-utils": "0.2.4",
"fluxible-addons-react": "0.2.8",
Expand Down
50 changes: 50 additions & 0 deletions precompress-file.sh
@@ -0,0 +1,50 @@
#!/bin/bash
set -e

if [ -x "$(command -v brotli)" ]; then
BROTLI=brotli
elif [ -x "$(command -v bro)" ]; then
BROTLI=bro
fi
if [ -x "$(command -v zopfli)" ]; then
ZOPFLI=zopfli
fi
if [ -x "$(command -v advpng)" ]; then
ADVPNG=advpng
fi

# settings
ZOPFLI_ITERATIONS=15
BROTLI_LEVEL=20
PNG_QUALITY=95
PNG_ZOPFLI_ITERATIONS=15
SKIP_COMPRESS_REGEXP="\.map$"

[ -f "$1" ] || exit 1

[[ "$1" =~ $SKIP_COMPRESS_REGEXP ]] && exit 0

TMPDIR="$(mktemp -d /tmp/precompress-XXXXXXXXXX)"
function cleanup {
rm -rf "$TMPDIR"
}
trap cleanup EXIT

if [[ "$1" =~ \.png$ ]]; then
TMPFILE="$TMPDIR/image.png"
if pngquant --speed 1 --quality $PNG_QUALITY - < "$1" > "$TMPFILE"; then :; else
[[ $? != 99 ]] && exit 1
fi
[ -n "$ADVPNG" ] && $ADVPNG --recompress --shrink-insane --iter=$PNG_ZOPFLI_ITERATIONS --quiet "$TMPFILE" || exit 1
mv "$TMPFILE" "$1" || exit 2
else
if [[ "$1" =~ \.svg$ ]]; then
export PATH+=:$(dirname "$0")/node_modules/.bin
svgo --multipass --disable removeUselessDefs --disable=cleanupIDs --config '{"useShortTags":false}' --output "$TMPDIR/file.svg" "$1" &&
mv "$TMPDIR/file.svg" "$1" || exit 3
fi
[ -n "$ZOPFLI" ] && $ZOPFLI -i$ZOPFLI_ITERATIONS "$1" -c > "$TMPDIR/file.gz" && mv "$TMPDIR/file.gz" "$1.gz" || exit 4
[ -n "$BROTLI" ] && $BROTLI --quality $BROTLI_LEVEL --input "$1" --output "$TMPDIR/file.br" && mv "$TMPDIR/file.br" "$1.br" || exit 5
fi

exit 0
14 changes: 14 additions & 0 deletions precompress.sh
@@ -0,0 +1,14 @@
#!/bin/bash
set -e

if [ ! -d "$1" ]; then
exit 1
fi

if [ $(uname) == Linux ]; then
NUMCPUS=$(grep -c "^processor" /proc/cpuinfo)
else
NUMCPUS=$(sysctl -n hw.ncpu)
fi

find "$1" -type f -and -not -name "*.gz" -and -not -name "*.br" -print0 | xargs -0 -n1 -P$NUMCPUS nice ./precompress-file.sh
5 changes: 4 additions & 1 deletion server/server.js
Expand Up @@ -42,7 +42,10 @@ const app = express();
function setUpStaticFolders() {
const staticFolder = path.join(process.cwd(), '_static');
// Sert cache for 1 week
app.use(config.APP_PATH, express.static(staticFolder, { maxAge: 604800000 }));
app.use(config.APP_PATH, express.static(staticFolder, {
maxAge: 604800000,
precompressed: [{ encoding: 'br', extension: '.br' }, { encoding: 'gzip', extension: '.gz' }],
}));
}

function setUpMiddleware() {
Expand Down

0 comments on commit 737e59d

Please sign in to comment.