Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ addons:
packages:
- google-chrome-stable
script:
- npm run js_prod
- npm run build
- npm run test
- xvfb-run wct
- if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct --plugin sauce; fi
Expand Down
7 changes: 7 additions & 0 deletions gulp/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
var path = require('path');

module.exports = {
static_dir: path.resolve(__dirname, '../..'),
polymer_dir: path.resolve(__dirname, '..'),
build_dir: path.resolve(__dirname, '../build'),
};
File renamed without changes.
107 changes: 107 additions & 0 deletions gulp/tasks/build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import buble from 'gulp-buble';
import uglify from 'gulp-uglify';
import { gulp as cssSlam } from 'css-slam';
import gulp from 'gulp';
import filter from 'gulp-filter';
import htmlMinifier from 'gulp-html-minifier';
import gulpif from 'gulp-if';
import { PolymerProject, HtmlSplitter } from 'polymer-build';
import {
composeStrategies,
generateShellMergeStrategy,
} from 'polymer-bundler';
import mergeStream from 'merge-stream';
import rename from 'gulp-rename';

import polymerConfig from '../../polymer';

function minifyStream(stream) {
const sourcesHtmlSplitter = new HtmlSplitter();
return stream
.pipe(sourcesHtmlSplitter.split())
.pipe(gulpif(/\.js$/, buble()))
.pipe(gulpif(/\.js$/, uglify({ sourceMap : false })))
.pipe(gulpif(/\.css$/, cssSlam()))
.pipe(gulpif(/\.html$/, cssSlam()))
.pipe(gulpif(/\.html$/, htmlMinifier({
collapseWhitespace: true,
removeComments: true
})))
.pipe(sourcesHtmlSplitter.rejoin());
}

function renamePanel(path) {
// Rename panels to be panels/* and not their subdir
if (path.basename.substr(0, 9) === 'ha-panel-' && path.extname === '.html') {
path.dirname = 'panels/';
}

// Rename frontend
if (path.dirname === 'src' && path.basename === 'home-assistant' &&
path.extname === '.html') {
path.dirname = '';
path.basename = 'frontend';
}
}

/**
* Polymer build strategy to strip imports, even if explictely imported
*/
function generateStripStrategy(urls) {
return (bundles) => {
for (const bundle of bundles) {
for (const url of urls) {
bundle.stripImports.add(url);
}
}
return bundles;
};
}

/**
* Polymer build strategy to strip everything but the entrypoints
* for bundles that match a specific entry point.
*/
function stripAllButEntrypoint(entryPoint) {
return (bundles) => {
for (const bundle of bundles) {
if (bundle.entrypoints.size === 1 &&
bundle.entrypoints.has(entryPoint)) {
for (const file of bundle.files) {
if (!bundle.entrypoints.has(file)) {
bundle.stripImports.add(file);
}
}
}
}
return bundles;
};
}


gulp.task('build', ['ru_all'], () => {
const strategy = composeStrategies([
generateShellMergeStrategy(polymerConfig.shell),
generateStripStrategy([
'bower_components/font-roboto/roboto.html',
'bower_components/paper-styles/color.html',
]),
stripAllButEntrypoint('panels/hassio/ha-panel-hassio.html')
]);
const project = new PolymerProject(polymerConfig);

return mergeStream(minifyStream(project.sources()),
minifyStream(project.dependencies()))
.pipe(project.bundler({
strategy,
strip: true,
sourcemaps: false,
stripComments: true,
inlineScripts: true,
inlineCss: true,
implicitStrip: true,
}))
.pipe(rename(renamePanel))
.pipe(filter(['**', '!src/entrypoint.html']))
.pipe(gulp.dest('build/'));
});
6 changes: 6 additions & 0 deletions gulp/tasks/clean.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import del from 'del';
import gulp from 'gulp';

gulp.task('clean', () => {
return del(['build', 'build-temp']);
});
9 changes: 9 additions & 0 deletions gulp/tasks/default.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import gulp from 'gulp';
import runSequence from 'run-sequence';

gulp.task('default', () => {
return runSequence.use(gulp)(
'clean',
'build',
);
});
110 changes: 110 additions & 0 deletions gulp/tasks/gen-service-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
Generate a caching service worker for HA

Will be called as part of build_frontend.

Expects home-assistant-polymer repo as submodule of HA repo.
Creates a caching service worker based on the CURRENT content of HA repo.
Output service worker to build/service_worker.js

TODO:
- Use gulp streams
- Fix minifying the stream
*/
var gulp = require('gulp');
var crypto = require('crypto');
var fs = require('fs');
var path = require('path');
var swPrecache = require('sw-precache');
var uglifyJS = require('uglify-js');

const config = require('../config');

const DEV = !!JSON.parse(process.env.BUILD_DEV || 'true');

var rootDir = config.static_dir;
var panelDir = path.resolve(rootDir, 'panels');

var dynamicUrlToDependencies = {
'/': [
rootDir + '/frontend.html',
rootDir + '/core.js',
rootDir + '/compatibility.js',
],
};

var staticFingerprinted = [
'frontend.html',
'mdi.html',
'core.js',
'compatibility.js',
];

// These panels will always be registered inside HA and thus can
// be safely assumed to be able to preload.
var panelsFingerprinted = [
'map', 'dev-event', 'dev-info', 'dev-service', 'dev-state', 'dev-template',
'dev-mqtt',
];

function md5(filename) {
return crypto.createHash('md5')
.update(fs.readFileSync(filename)).digest('hex');
}

gulp.task('gen-service-worker', () => {
// Create fingerprinted versions of our dependencies.
staticFingerprinted.forEach(fn => {
var parts = path.parse(fn);
var hash = md5(rootDir + '/' + parts.name + parts.ext);
var url = '/static/' + parts.name + '-' + hash + parts.ext;
var fpath = rootDir + '/' + parts.name + parts.ext;
dynamicUrlToDependencies[url] = [fpath];
});

panelsFingerprinted.forEach(panel => {
var fpath = panelDir + '/ha-panel-' + panel + '.html';
var hash = md5(fpath);
var url = '/frontend/panels/' + panel + '-' + hash + '.html';
dynamicUrlToDependencies[url] = [fpath];
});

var options = {
navigateFallback: '/',
navigateFallbackWhitelist: [/^((?!(static|api|local|service_worker.js|manifest.json)).)*$/],
dynamicUrlToDependencies: dynamicUrlToDependencies,
staticFileGlobs: [
rootDir + '/icons/favicon.ico',
rootDir + '/icons/favicon-192x192.png',
rootDir + '/webcomponents-lite.min.js',
rootDir + '/fonts/roboto/Roboto-Light.ttf',
rootDir + '/fonts/roboto/Roboto-Medium.ttf',
rootDir + '/fonts/roboto/Roboto-Regular.ttf',
rootDir + '/fonts/roboto/Roboto-Bold.ttf',
rootDir + '/images/card_media_player_bg.png',
],
stripPrefix: '..',
replacePrefix: 'static',
verbose: true,
};

var devBase = 'console.warn("Service worker caching disabled in development")';

var swHass = fs.readFileSync(path.resolve(__dirname, '../service-worker.js.tmpl'), 'UTF-8');

var genPromise = DEV ? Promise.resolve(devBase) : swPrecache.generate(options);

genPromise = genPromise.then(swString => swString + '\n' + swHass);

// Fix this
// if (!DEV) {
// genPromise = genPromise.then(
// swString => uglifyJS.minify(swString, { fromString: true }).code);
// }

genPromise.then(swString => {
fs.writeFileSync(path.resolve(config.build_dir, 'service_worker.js'), swString);
});

return genPromise;
});
66 changes: 17 additions & 49 deletions script/vulcanize.js → gulp/tasks/hassio-panel.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
#!/usr/bin/env node

/*
TODO:
- Use gulp streams
- Use polymer bundler to vulcanize
*/
var gulp = require('gulp');
var Vulcanize = require('vulcanize');
var minify = require('html-minifier');
var hyd = require('hydrolysis');
var fs = require('fs');

if (!fs.existsSync('build')) {
fs.mkdirSync('build');
}
if (!fs.existsSync('build/panels')) {
fs.mkdirSync('build/panels');
}

function minifyHTML(html) {
return minify.minify(html, {
customAttrAssign: [/\$=/],
Expand All @@ -33,30 +29,12 @@ const baseVulcanOptions = {
stripComments: true,
};

const panelVulcan = new Vulcanize({
inlineScripts: true,
inlineCss: true,
implicitStrip: true,
stripComments: true,
stripExcludes: [
'panels/hassio/hassio-main.html'
],
});

const baseExcludes = [
'bower_components/font-roboto/roboto.html',
'bower_components/paper-styles/color.html',
];

const toProcess = [
// This is the main entry point
{
source: './src/home-assistant.html',
output: './build/frontend.html',
vulcan: new Vulcanize(Object.assign({}, baseVulcanOptions, {
stripExcludes: baseExcludes,
})),
},
// This is the Hass.io configuration panel
// It's build standalone because it is embedded in the supervisor.
{
Expand All @@ -71,14 +49,6 @@ const toProcess = [
},
];

fs.readdirSync('./panels').forEach((panel) => {
toProcess.push({
source: `./panels/${panel}/ha-panel-${panel}.html`,
output: `./build/panels/ha-panel-${panel}.html`,
vulcan: panelVulcan,
});
});

function vulcanizeEntry(entry) {
return new Promise((resolve, reject) => {
console.log('Processing', entry.source);
Expand All @@ -95,16 +65,14 @@ function vulcanizeEntry(entry) {
});
}

// Fetch all dependencies of main app and exclude them from panels
hyd.Analyzer.analyze('src/home-assistant.html')
.then(function (analyzer) {
return analyzer._getDependencies('src/home-assistant.html');
})
.then((deps) => {
panelVulcan.stripExcludes = panelVulcan.stripExcludes.concat(deps);
})
// Chain all vulcanizing work as promises
.then(() => toProcess.reduce(
(p, entry) => p.then(() => vulcanizeEntry(entry)),
Promise.resolve()))
.catch(err => console.error('Something went wrong!', err));
gulp.task('hassio-panel', () => {
if (!fs.existsSync('build-temp')) {
fs.mkdirSync('build-temp');
}

toProcess.reduce(
(p, entry) => p.then(() => vulcanizeEntry(entry)),
Promise.resolve());

return toProcess;
});
29 changes: 29 additions & 0 deletions gulp/tasks/rollup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import gulp from 'gulp';
import rollupEach from 'gulp-rollup-each';
import rollupConfig from '../../rollup.config';

gulp.task('run_rollup', () => {
return gulp.src([
'js/core.js',
'js/compatibility.js',
'js/editor/editor.js',
'demo_data/demo_data.js',
])
.pipe(rollupEach(rollupConfig))
.pipe(gulp.dest('build-temp'));
});

gulp.task('ru_all', ['run_rollup'], () => {
gulp.src([
'build-temp/core.js',
'build-temp/compatibility.js',
])
.pipe(gulp.dest('build/'));
});

gulp.task('watch_ru_all', ['ru_all'], () => {
gulp.watch([
'js/**/*.js',
'demo_data/**/*.js'
], ['ru_all']);
});
Loading