Pre-construct frontend index.html (#609)

* Pre-construct frontend index.html

* Only preload things that matter

* Fix entry point in dev mode

* Template Service worker url

* Update referenced service worker
This commit is contained in:
Paulus Schoutsen 2017-11-11 13:30:14 -08:00 committed by GitHub
parent f106767eae
commit 3701683d4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 193 additions and 17 deletions

8
gulp/common/md5.js Normal file
View File

@ -0,0 +1,8 @@
const fs = require('fs');
const crypto = require('crypto');
module.exports = function md5(filename) {
return crypto.createHash('md5')
.update(fs.readFileSync(filename)).digest('hex');
};

View File

@ -1,7 +1,8 @@
var path = require('path');
module.exports = {
static_dir: path.resolve(__dirname, '../..'),
polymer_dir: path.resolve(__dirname, '..'),
build_dir: path.resolve(__dirname, '../build'),
output: path.resolve(__dirname, '../hass_frontend'),
output_es5: path.resolve(__dirname, '../hass_frontend_es5'),
};

View File

@ -0,0 +1,44 @@
const gulp = require('gulp');
const replace = require('gulp-batch-replace');
const path = require('path');
const url = require('url');
const config = require('../config');
const md5 = require('../common/md5.js');
const buildReplaces = {
'/home-assistant-polymer/build/core.js': 'core.js',
'/home-assistant-polymer/src/home-assistant.html': 'frontend.html',
};
function generateIndex(es6) {
const targetPath = es6 ? config.output : config.output_es5;
const targetUrl = es6 ? '/frontend_latest/' : '/frontend_es5/';
const toReplace = [
['/home-assistant-polymer/hass_frontend/mdi.html',
`/static/mdi-${md5(path.resolve(config.output, 'mdi.html'))}.html`],
['/home-assistant-polymer/build-temp/compatibility.js',
`/static/compatibility-${md5(path.resolve(config.output_es5, 'compatibility.js'))}.js`],
];
if (!es6) {
toReplace.push([
'/service_worker.js', '/service_worker_es5.js'
]);
}
for (const [replaceSearch, filename] of Object.entries(buildReplaces)) {
const parsed = path.parse(filename);
const hash = md5(path.resolve(targetPath, filename));
toReplace.push([
replaceSearch,
url.resolve(targetUrl, `${parsed.name}-${hash}${parsed.ext}`)]);
}
gulp.src(path.resolve(config.polymer_dir, 'index.html'))
.pipe(replace(toReplace))
.pipe(gulp.dest(targetPath));
}
gulp.task('gen-index-html-es5', generateIndex.bind(null, /* es6= */ false));
gulp.task('gen-index-html', generateIndex.bind(null, /* es6= */ true));

View File

@ -12,11 +12,11 @@ TODO:
- Fix minifying the stream
*/
const gulp = require('gulp');
const crypto = require('crypto');
const file = require('gulp-file');
const fs = require('fs');
const path = require('path');
const swPrecache = require('sw-precache');
const md5 = require('../common/md5.js');
const DEV = !!JSON.parse(process.env.BUILD_DEV || 'true');
@ -45,11 +45,6 @@ const panelsFingerprinted = [
'dev-mqtt', 'kiosk',
];
function md5(filename) {
return crypto.createHash('md5')
.update(fs.readFileSync(filename)).digest('hex');
}
function processStatic(fn, rootDir, urlDir) {
const parts = path.parse(fn);
const base = parts.dir.length > 0 ? parts.dir + '/' + parts.name : parts.name;
@ -117,7 +112,7 @@ function generateServiceWorker(es6) {
}
],
stripPrefix: baseRootDir,
replacePrefix: 'static',
replacePrefix: '/static',
verbose: true,
// Allow our users to refresh to get latest version.
clientsClaim: true,

121
index.html Normal file
View File

@ -0,0 +1,121 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Home Assistant</title>
<link rel='manifest' href='/manifest.json'>
<link rel='icon' href='/static/icons/favicon.ico'>
<link rel='apple-touch-icon' sizes='180x180'
href='/static/icons/favicon-apple-180x180.png'>
<link rel="mask-icon" href="/static/icons/mask-icon.svg" color="#3fbbf4">
<link rel='preload' href='/home-assistant-polymer/build/core.js' as='script'/>
<link rel='preload' href='/static/fonts/roboto/Roboto-Regular.ttf' as='font' crossorigin />
<link rel='preload' href='/static/fonts/roboto/Roboto-Medium.ttf' as='font' crossorigin />
<meta name='apple-mobile-web-app-capable' content='yes'>
<meta name="msapplication-square70x70logo" content="/static/icons/tile-win-70x70.png"/>
<meta name="msapplication-square150x150logo" content="/static/icons/tile-win-150x150.png"/>
<meta name="msapplication-wide310x150logo" content="/static/icons/tile-win-310x150.png"/>
<meta name="msapplication-square310x310logo" content="/static/icons/tile-win-310x310.png"/>
<meta name="msapplication-TileColor" content="#3fbbf4ff"/>
<meta name='mobile-web-app-capable' content='yes'>
<meta name='viewport' content='width=device-width, user-scalable=no'>
<meta name='theme-color' content='{{ theme_color }}'>
<style>
body {
font-family: 'Roboto', 'Noto', sans-serif;
font-weight: 400;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
margin: 0;
padding: 0;
}
#ha-init-skeleton::before {
display: block;
content: "";
height: 48px;
background-color: {{ theme_color }};
}
#ha-init-skeleton .message {
transition: font-size 2s;
font-size: 0;
padding: 24px;
}
#ha-init-skeleton.error .message {
font-size: 16px;
}
#ha-init-skeleton a {
color: {{ theme_color }};
text-decoration: none;
font-weight: bold;
}
</style>
<script>
function initError() {
document.getElementById('ha-init-skeleton').classList.add('error');
};
window.noAuth = {{ no_auth }};
window.Polymer = {
lazyRegister: true,
useNativeCSSProperties: true,
dom: 'shadow',
suppressTemplateNotifications: true,
suppressBindingNotifications: true,
};
</script>
</head>
<body>
<div id='ha-init-skeleton'>
<div class='message'>
Home Assistant had trouble<br>connecting to the server.<br><br>
<a href='/'>TRY AGAIN</a>
</div>
</div>
<home-assistant></home-assistant>
{# <script src='/home-assistant-polymer/build/_demo_data_compiled.js'></script> -#}
{% if not latest -%}
<script>
var compatibilityRequired = (typeof Object.assign != 'function');
if (compatibilityRequired) {
var e = document.createElement('script');
e.onerror = initError;
e.src = '/home-assistant-polymer/build-temp/compatibility.js';
document.head.appendChild(e);
}
</script>
{% endif -%}
<script src='/home-assistant-polymer/build/core.js'></script>
{% if not dev_mode and not latest -%}
<script src='/frontend_es5/custom-elements-es5-adapter.js'></script>
{% endif -%}
<script>
var webComponentsSupported = (
'customElements' in window &&
'import' in document.createElement('link') &&
'content' in document.createElement('template'));
if (!webComponentsSupported) {
var e = document.createElement('script');
e.onerror = initError;
e.src = '/static/webcomponents-lite.js';
document.head.appendChild(e);
}
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('/service_worker.js');
});
}
</script>
<link rel='import' href='/home-assistant-polymer/src/home-assistant.html' onerror='initError()'>
{% if panel_url -%}
<link rel='import' href='{{ panel_url }}' onerror='initError()' async>
{% endif -%}
<link rel='import' href='/home-assistant-polymer/hass_frontend/mdi.html' async>
{% for extra_url in extra_urls -%}
<link rel='import' href='{{ extra_url }}' async>
{% endfor -%}
</body>
</html>

View File

@ -38,6 +38,7 @@
"eslint-plugin-react": "^7.0.0",
"gulp": "^3.9.1",
"gulp-babel": "^7.0.0",
"gulp-batch-replace": "^0.0.0",
"gulp-file": "^0.3.0",
"gulp-filter": "^5.0.1",
"gulp-foreach": "^0.1.0",

View File

@ -79,3 +79,5 @@ echo "CREATED_AT = `date +%s`" >> $OUTPUT_DIR_ES5/__init__.py
# Generate the MD5 hash of the new frontend
script/fingerprint_frontend.py --base_dir $OUTPUT_DIR
script/fingerprint_frontend.py --base_dir $OUTPUT_DIR_ES5
gulp gen-index-html
gulp gen-index-html-es5

View File

@ -7,28 +7,26 @@ import hashlib
import json
import argparse
from os import path
import re
parser = argparse.ArgumentParser(description='Generate fingerprints of frontend files.')
parser.add_argument('--base_dir', type=str, help='Base dir to look for files.', default='hass_frontend')
args = parser.parse_args()
base_dir = args.base_dir + '/'
fingerprint_file = path.join(base_dir, '__init__.py')
panel_match = re.compile(r'ha-panel-((\w|-)+)\.html')
def fingerprint():
"""Fingerprint the frontend files."""
files = (glob.glob(base_dir + '**/*.html') +
glob.glob(base_dir + '*.html') +
glob.glob(base_dir + 'core.js') +
glob.glob(base_dir + 'compatibility.js'))
"""Fingerprint the panels."""
files = glob.glob(base_dir + 'panels/*.html')
md5s = OrderedDict()
for fil in sorted(files):
name = fil[len(base_dir):]
panel = panel_match.search(fil).groups(0)[0]
with open(fil) as fp:
md5 = hashlib.md5(fp.read().encode('utf-8')).hexdigest()
md5s[name] = md5
md5s[panel] = md5
template = "FINGERPRINTS = {}\n"
result = template.format(json.dumps(md5s, indent=4))

View File

@ -2874,7 +2874,7 @@ etag@~1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
event-stream@^3.0.20, event-stream@^3.3.1:
event-stream@^3.0.20, event-stream@^3.3.1, event-stream@latest:
version "3.3.4"
resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571"
dependencies:
@ -3657,6 +3657,12 @@ gulp-babel@^7.0.0:
through2 "^2.0.0"
vinyl-sourcemaps-apply "^0.2.0"
gulp-batch-replace@^0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/gulp-batch-replace/-/gulp-batch-replace-0.0.0.tgz#7e9826ad928862722c1eacb4421b4127bffd643e"
dependencies:
event-stream latest
gulp-file@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/gulp-file/-/gulp-file-0.3.0.tgz#e8c4d763f126fb3332fc416e3d1ef46ed67d8d0d"