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
3 changes: 3 additions & 0 deletions bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
},
"roboto-fontface": {
"main": "css/roboto-fontface.css"
},
"google-closure-library": {
"main": ["closure/goog/base.js", "closure/goog/deps.js"]
}
},
"devDependencies": {
Expand Down
182 changes: 150 additions & 32 deletions build/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ import gulpHtmlmin from 'gulp-htmlmin';
import gulpUglify from 'gulp-uglify';
import gulpIf from 'gulp-if';
import gulpUseref from 'gulp-useref';
import gulpRev from 'gulp-rev';
import gulpRevReplace from 'gulp-rev-replace';
import uglifySaveLicense from 'uglify-save-license';
import GulpRevAll from 'gulp-rev-all';
import mergeStream from 'merge-stream';
import path from 'path';
import uglifySaveLicense from 'uglify-save-license';

import conf from './conf';
import {multiDest} from './multidest';
Expand All @@ -41,19 +41,67 @@ gulp.task('build', ['backend:prod', 'build-frontend']);
gulp.task('build:cross', ['backend:prod:cross', 'build-frontend:cross']);

/**
* Builds production version of the frontend application for the current architecture.
* Builds production version of the frontend application for the default architecture.
*/
gulp.task(
'build-frontend', ['localize', 'locales-for-backend'], function() { return doRevision(); });

/**
* Builds production version of the frontend application for all supported architectures.
*/
gulp.task('build-frontend:cross', ['localize:cross', 'locales-for-backend:cross'], function() {
return doRevision();
});

/**
* Localizes all pre-created frontend copies for the default arch, so that they are ready to serve.
*/
gulp.task('localize', ['frontend-copies'], function() {
return localize([path.join(conf.paths.distPre, conf.arch.default, 'public')]);
});

/**
* Localizes all pre-created frontend copies in all cross-arch directories, so that they are ready
* to serve.
*/
gulp.task('localize:cross', ['frontend-copies:cross'], function() {
return localize(conf.arch.list.map((arch) => path.join(conf.paths.distPre, arch, 'public')));
});

/**
* Copies the locales configuration to the default arch directory.
* This configuration file is then used by the backend to localize dashboard.
*/
gulp.task('locales-for-backend', ['clean-dist'], function() {
return localesForBackend([conf.paths.dist]);
});

/**
* Copies the locales configuration to each arch directory.
* This configuration file is then used by the backend to localize dashboard.
*/
gulp.task('locales-for-backend:cross', ['clean-dist'], function() {
return localesForBackend(conf.paths.distCross);
});

/**
* Builds production version of the frontend application for the default architecture
* (one copy per locale) and plcaes it under .tmp/dist , preparing it for localization and revision.
*/
gulp.task('build-frontend', ['fonts', 'icons', 'assets', 'index:prod', 'clean-dist'], function() {
return buildFrontend(conf.paths.distPublic);
gulp.task('frontend-copies', ['fonts', 'icons', 'assets', 'index:prod', 'clean-dist'], function() {
return createFrontendCopies([path.join(conf.paths.distPre, conf.arch.default, 'public')]);
});

/**
* Builds production version of the frontend application for all architecures.
* Builds production versions of the frontend application for all architecures
* (one copy per locale) and places them under .tmp, preparing them for localization and revision.
*/
gulp.task(
'build-frontend:cross',
['fonts:cross', 'icons:cross', 'assets:cross', 'index:prod', 'clean-dist'],
function() { return buildFrontend(conf.paths.distPublicCross); });
'frontend-copies:cross',
['fonts:cross', 'icons:cross', 'assets:cross', 'index:prod', 'clean-dist'], function() {
return createFrontendCopies(
conf.arch.list.map((arch) => path.join(conf.paths.distPre, arch, 'public')));
});

/**
* Copies assets to the dist directory for current architecture.
Expand All @@ -69,24 +117,22 @@ gulp.task(
/**
* Copies icons to the dist directory for current architecture.
*/
gulp.task('icons', ['clean-dist'], function() { return icons(conf.paths.iconsDistPublic); });
gulp.task('icons', ['clean-dist'], function() { return icons([conf.paths.distPublic]); });

/**
* Copies icons to the dist directory for all architectures.
*/
gulp.task(
'icons:cross', ['clean-dist'], function() { return icons(conf.paths.iconsDistPublicCross); });
gulp.task('icons:cross', ['clean-dist'], function() { return icons(conf.paths.distPublicCross); });

/**
* Copies fonts to the dist directory for current architecture.
*/
gulp.task('fonts', ['clean-dist'], function() { return fonts(conf.paths.fontsDistPublic); });
gulp.task('fonts', ['clean-dist'], function() { return fonts([conf.paths.distPublic]); });

/**
* Copies fonts to the dist directory for all architectures.
*/
gulp.task(
'fonts:cross', ['clean-dist'], function() { return fonts(conf.paths.fontsDistPublicCross); });
gulp.task('fonts:cross', ['clean-dist'], function() { return fonts(conf.paths.distPublicCross); });

/**
* Cleans all build artifacts.
Expand All @@ -98,20 +144,27 @@ gulp.task('clean', ['clean-dist'], function() {
/**
* Cleans all build artifacts in the dist/ folder.
*/
gulp.task('clean-dist', function() { return del([conf.paths.distRoot]); });
gulp.task('clean-dist', function() { return del([conf.paths.distRoot, conf.paths.distPre]); });

/**
* Builds production version of the frontend application.
* Builds production version of the frontend application and copies it to all
* the specified outputDirs, creating one copy per (outputDir x locale) tuple.
*
* Following steps are done here:
* 1. Vendor CSS and JS files are concatenated and minified.
* 2. index.html is minified.
* 3. CSS and JS assets are suffixed with version hash.
* 4. Everything is saved in the dist directory.
* @param {string|!Array<string>} outputDirs
* 3. Everything is saved in the .tmp/dist directory, ready to be localized and revisioned.
*
* @param {!Array<string>} outputDirs
* @return {stream}
*/
function buildFrontend(outputDirs) {
function createFrontendCopies(outputDirs) {
// create an output for each locale
let localizedOutputDirs = outputDirs.reduce((localizedDirs, outputDir) => {
return localizedDirs.concat(
conf.translations.map((translation) => { return path.join(outputDir, translation.key); }));
}, []);

let searchPath = [
// To resolve local paths.
path.relative(conf.paths.base, conf.paths.prodTmp),
Expand All @@ -123,44 +176,109 @@ function buildFrontend(outputDirs) {
.pipe(gulpUseref({searchPath: searchPath}))
.pipe(gulpIf('**/vendor.css', gulpMinifyCss()))
.pipe(gulpIf('**/vendor.js', gulpUglify({preserveComments: uglifySaveLicense})))
.pipe(gulpIf(['**/*.js', '**/*.css'], gulpRev()))
.pipe(gulpUseref({searchPath: searchPath}))
.pipe(gulpRevReplace())
.pipe(gulpIf('*.html', gulpHtmlmin({
removeComments: true,
collapseWhitespace: true,
conservativeCollapse: true,
})))
.pipe(multiDest(outputDirs));
.pipe(multiDest(localizedOutputDirs));
}

/**
* @param {string|!Array<string>} outputDirs
* Creates revisions of all .js anc .css files at once (for production).
* Replaces the occurances of those files in index.html with their new names.
* index.html does not get renamed in the process.
* The processed files are then moved to the dist directory.
* @return {stream}
*/
function doRevision() {
// Do not update references other than in index.html. Do not rev index.html itself.
let revAll =
new GulpRevAll({dontRenameFile: ['index.html'], dontSearchFile: [/^(?!.*index\.html$).*$/]});
return gulp.src([path.join(conf.paths.distPre, '**'), '!**/assets/**/*'])
.pipe(revAll.revision())
.pipe(gulp.dest(conf.paths.distRoot));
}

/**
* Copies the localized app.js files for each supported language in outputDir/<locale>/static
* for each of the specified output dirs.
* @param {!Array<string>} outputDirs - list of all arch directories
* @return {stream}
*/
function localize(outputDirs) {
let streams = conf.translations.map((translation) => {
let localizedOutputDirs =
outputDirs.map((outputDir) => { return path.join(outputDir, translation.key, 'static'); });
return gulp.src(path.join(conf.paths.i18nProd, translation.key, '*.js'))
.pipe(multiDest(localizedOutputDirs));
});

return mergeStream.apply(null, streams);
}

/**
* Copies the locales configuration file at the base of each arch directory, next to
* all of the localized subdirs. This file is meant to be used by the backend binary
* to compare against and determine the right locale to serve at runtime.
* @param {!Array<string>} outputDirs - list of all arch directories
* @return {stream}
*/
function localesForBackend(outputDirs) {
return gulp.src(path.join(conf.paths.base, 'i18n', '*.json')).pipe(multiDest(outputDirs));
}

/**
* Copies the assets files to all dist directories per arch and locale.
* @param {!Array<string>} outputDirs
* @return {stream}
*/
function assets(outputDirs) {
let localizedOutputDirs = createLocalizedOutputs(outputDirs);
return gulp.src(path.join(conf.paths.assets, '/**/*'), {base: conf.paths.app})
.pipe(multiDest(outputDirs));
.pipe(multiDest(localizedOutputDirs));
}

/**
* @param {string|!Array<string>} outputDirs
* Copies the icons files to all dist directories per arch and locale.
* @param {!Array<string>} outputDirs
* @return {stream}
*/
function icons(outputDirs) {
let localizedOutputDirs = createLocalizedOutputs(outputDirs, 'static');
return gulp
.src(
path.join(conf.paths.materialIcons, '/**/*.+(woff2|woff|eot|ttf)'),
{base: conf.paths.materialIcons})
.pipe(multiDest(outputDirs));
.pipe(multiDest(localizedOutputDirs));
}

/**
* @param {string|!Array<string>} outputDirs
* Copies the font files to all dist directories per arch and locale.
* @param {!Array<string>} outputDirs
* @return {stream}
*/
function fonts(outputDirs) {
let localizedOutputDirs = createLocalizedOutputs(outputDirs, 'fonts');
return gulp
.src(path.join(conf.paths.robotoFonts, '/**/*.+(woff2)'), {base: conf.paths.robotoFonts})
.pipe(multiDest(outputDirs));
.pipe(multiDest(localizedOutputDirs));
}

/**
* Returns one subdirectory path for each supported locale inside all of the specified
* outputDirs. Optionally, a subdirectory structure can be passed to append after each locale path.
* @param {!Array<string>} outputDirs
* @param {undefined|string} opt_subdir - an optional sub directory inside each locale directory.
* @return {!Array<string>} localized output directories
*/
function createLocalizedOutputs(outputDirs, opt_subdir) {
return outputDirs.reduce((localizedDirs, outputDir) => {
return localizedDirs.concat(conf.translations.map((translation) => {
if (opt_subdir) {
return path.join(outputDir, translation.key, opt_subdir);
}
return path.join(outputDir, translation.key);
}));
}, []);
}
21 changes: 15 additions & 6 deletions build/conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
*/
import path from 'path';

/**
* Load the i18n and l10n configuration. Used when dashboard is built in production.
*/
let localization = require('../i18n/locale_conf.json');

/**
* Base path for all other paths.
*/
Expand Down Expand Up @@ -154,6 +159,13 @@ export default {
!!process.env.TRAVIS && process.env.TRAVIS_PULL_REQUEST === 'false',
},

/**
* Configuration for i18n & l10n.
*/
translations: localization.translations.map((translation) => {
return {path: path.join(basePath, 'i18n', translation.file), key: translation.key};
}),

/**
* Absolute paths to known directories, e.g., to source directory.
*/
Expand All @@ -172,21 +184,17 @@ export default {
deploySrc: path.join(basePath, 'src/deploy'),
dist: path.join(basePath, 'dist', arch.default),
distCross: arch.list.map((arch) => path.join(basePath, 'dist', arch)),
distPre: path.join(basePath, '.tmp/dist'),
distPublic: path.join(basePath, 'dist', arch.default, 'public'),
distPublicCross: arch.list.map((arch) => path.join(basePath, 'dist', arch, 'public')),
distRoot: path.join(basePath, 'dist'),
externs: path.join(basePath, 'src/app/externs'),
fontsDistPublic: path.join(basePath, 'dist', arch.default, 'public/fonts'),
fontsDistPublicCross:
arch.list.map((arch) => path.join(basePath, 'dist', arch, 'public/fonts')),
frontendSrc: path.join(basePath, 'src/app/frontend'),
frontendTest: path.join(basePath, 'src/test/frontend'),
goTools: path.join(basePath, '.tools/go'),
goWorkspace: path.join(basePath, '.go_workspace'),
hyperkube: path.join(basePath, 'build/hyperkube.sh'),
iconsDistPublic: path.join(basePath, 'dist', arch.default, 'public/static'),
iconsDistPublicCross:
arch.list.map((arch) => path.join(basePath, 'dist', arch, 'public/static')),
i18nProd: path.join(basePath, '.tmp/i18n'),
integrationTest: path.join(basePath, 'src/test/integration'),
karmaConf: path.join(basePath, 'build/karma.conf.js'),
materialIcons: path.join(basePath, 'bower_components/material-design-icons/iconfont'),
Expand All @@ -198,5 +206,6 @@ export default {
serve: path.join(basePath, '.tmp/serve'),
src: path.join(basePath, 'src'),
tmp: path.join(basePath, '.tmp'),
xtbgenerator: path.join(basePath, '.tools/xtbgenerator/bin/XtbGenerator.jar'),
},
};
Loading