Skip to content

Commit 80a3e3e

Browse files
Implements compilation process for localized binaries.
1 parent 8bf25c5 commit 80a3e3e

File tree

3 files changed

+230
-132
lines changed

3 files changed

+230
-132
lines changed

buildtools/soy_files.js

Lines changed: 0 additions & 32 deletions
This file was deleted.

gulpfile.js

Lines changed: 222 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -12,144 +12,270 @@
1212
* limitations under the License.
1313
*/
1414

15-
var gulp = require('gulp'),
16-
streamqueue = require('streamqueue'),
17-
closureCompiler = require('gulp-closure-compiler'),
18-
cleanCSS = require('gulp-clean-css'),
19-
concatCSS = require('gulp-concat-css'),
20-
sass = require('gulp-sass'),
21-
del = require('del');
15+
const cleanCSS = require('gulp-clean-css');
16+
const closureBuilder = require('closure-builder');
17+
const closureCompiler = require('gulp-closure-compiler');
18+
const concatCSS = require('gulp-concat-css');
19+
const connect = require('gulp-connect');
20+
const fse = require('fs-extra');
21+
const flip = require('gulp-css-flip');
22+
const gulp = require('gulp');
23+
const path = require('path');
24+
const sass = require('gulp-sass');
25+
const streamqueue = require('streamqueue');
26+
const util = require('gulp-util');
2227

28+
const glob = closureBuilder.globSupport();
2329

2430
// The optimization level for the JS compiler.
2531
// Valid levels: WHITESPACE_ONLY, SIMPLE_OPTIMIZATIONS, ADVANCED_OPTIMIZATIONS.
26-
// TODO: Add ability to pass this in as a flag.
27-
var OPTIMIZATION_LEVEL = 'ADVANCED_OPTIMIZATIONS';
28-
32+
// This can be passed in as a flag:
33+
// $ gulp --compilation_level=WHITESPACE_ONLY
34+
const OPTIMIZATION_LEVEL = util.env.compilation_level ||
35+
'ADVANCED_OPTIMIZATIONS';
2936

3037
// For minified builds, wrap the output so we avoid leaking global variables.
31-
var OUTPUT_WRAPPER = OPTIMIZATION_LEVEL === 'WHITESPACE_ONLY' ?
38+
const OUTPUT_WRAPPER = OPTIMIZATION_LEVEL === 'WHITESPACE_ONLY' ?
3239
'%output%' : '(function() { %output% })();';
3340

3441
// Adds the firebase module requirement and exports firebaseui.
35-
var NPM_MODULE_WRAPPER = OPTIMIZATION_LEVEL === 'WHITESPACE_ONLY' ?
42+
const NPM_MODULE_WRAPPER = OPTIMIZATION_LEVEL === 'WHITESPACE_ONLY' ?
3643
'var firebase=require(\'firebase\');%output%module.exports=firebaseui;' :
3744
'(function() { var firebase=require(\'firebase\');%output% })();' +
3845
'module.exports=firebaseui;';
3946

40-
4147
// The path to Closure Compiler.
42-
var COMPILER_PATH = 'node_modules/google-closure-compiler/compiler.jar';
48+
const COMPILER_PATH = 'node_modules/google-closure-compiler/compiler.jar';
49+
50+
// The path to the temporary directory where intermediate results are stored.
51+
const TMP_DIR = 'out';
52+
53+
// The path to the temporary directory where intermediate results are stored.
54+
const DEST_DIR = 'dist';
55+
56+
// The locale that would be produced with no XTBs.
57+
const DEFAULT_LOCALE = 'en';
58+
59+
// The list of all locales that are supported.
60+
const ALL_LOCALES = ['ar-XB', 'ar', 'bg', 'ca', 'cs', 'da', 'de', 'el', 'en',
61+
'en-GB', 'en-XA', 'es-419', 'es', 'fa', 'fi', 'fil', 'fr', 'hi', 'hr', 'hu',
62+
'id', 'it', 'iw', 'ja', 'ko', 'lt', 'lv', 'nl', 'no', 'pl', 'pt-PT', 'pt',
63+
'ro', 'ru', 'sk', 'sl', 'sr', 'sv', 'th', 'tr', 'uk', 'vi', 'zh-CN',
64+
'zh-TW'];
4365

66+
// Compiles the Closure templates into JavaScript.
67+
gulp.task('build-soy', () => new Promise((resolve, reject) => {
68+
closureBuilder.build({
69+
name: 'soy_files',
70+
srcs: glob([
71+
'soy/*.soy'
72+
]),
73+
out: TMP_DIR,
74+
options: {
75+
soy: {
76+
shouldGenerateGoogMsgDefs: true,
77+
bidiGlobalDir: 1
78+
}
79+
},
80+
}, resolve);
81+
}));
82+
83+
// Default arguments to pass into Closure Compiler.
84+
const COMPILER_DEFAULT_ARGS = {
85+
compilation_level: OPTIMIZATION_LEVEL,
86+
language_out: 'ES5'
87+
};
88+
89+
/**
90+
* Invokes Closure Compiler.
91+
* @param {!Array<string>} srcs The JS sources to compile.
92+
* @param {string} out The path to the output JS file.
93+
* @param {!Object} args Additional arguments to Closure compiler.
94+
* @return {*} A stream that finishes when compliation finishes.
95+
*/
96+
function compile(srcs, out, args) {
97+
// Get
98+
const combinedArgs = Object.assign({}, COMPILER_DEFAULT_ARGS, args);
99+
return gulp
100+
.src(srcs)
101+
.pipe(closureCompiler({
102+
compilerPath: COMPILER_PATH,
103+
fileName: path.basename(out),
104+
compilerFlags: combinedArgs
105+
}))
106+
.pipe(gulp.dest(path.dirname(out)));
107+
}
44108

45109
/**
46-
* Concatenates JS files using the Closure Compiler.
47-
* @param {string} outFileName The name of the output file.
48-
* @param {string} wrapper The output wrapper to use after concatenating files.
49-
* @return {*}
110+
* Normalizes a locale ID for use in a file name (e.g. en-GB -> en_gb).
111+
* @param {string} locale
112+
* @return {string} The normalized locale ID.
50113
*/
51-
function concatJS(outFileName, wrapper) {
52-
return closureCompiler({
53-
compilerPath: COMPILER_PATH,
54-
fileName: outFileName,
55-
compilerFlags: {
56-
compilation_level: 'WHITESPACE_ONLY',
57-
language_out: 'ES5',
58-
output_wrapper: wrapper
59-
}
114+
function getLocaleForFileName(locale) {
115+
return locale.toLowerCase().replace(/-/g, '_');
116+
}
117+
118+
/**
119+
* Gets the path to the temporary JS file that contains all FirebaseUI code
120+
* but no external dependencies.
121+
* @param {string} locale
122+
*/
123+
function getTmpJsPath(locale) {
124+
const localeForFileName = getLocaleForFileName(locale);
125+
return `${TMP_DIR}/firebaseui__${localeForFileName}.js`;
126+
}
127+
128+
/**
129+
* Repeats a gulp task for all locales.
130+
* @param {string} taskName The gulp task name to generate. Any $ tokens will be
131+
* replaced with the language code (e.g. build-$ becomes build-fr, build-es,
132+
* etc.).
133+
* @param {!Array<string>} dependencies The gulp tasks that each operation
134+
* depends on. Any $ tokens will be replaced with the language code.
135+
* @param {function()} operation The function to execute.
136+
* @return {!Array<string>} The list of generated task names.
137+
*/
138+
function repeatTaskForAllLocales(taskName, dependencies, operation) {
139+
const tasks = [];
140+
ALL_LOCALES.forEach((locale) => {
141+
const replaceTokens = (name) => name.replace(/\$/g, locale);
142+
143+
const localeTaskName = replaceTokens(taskName);
144+
tasks.push(localeTaskName);
145+
const localeDependencies = dependencies.map(replaceTokens);
146+
147+
gulp.task(localeTaskName, localeDependencies, () => operation(locale));
60148
});
149+
return tasks;
61150
}
62151

152+
/**
153+
* Builds the core FirebaseUI binary in the given locale.
154+
* @param {string} locale
155+
*/
156+
function buildFirebaseUiJs(locale) {
157+
const flags = {
158+
closure_entry_point: 'firebaseui.auth.exports',
159+
define: `goog.LOCALE='${locale}'`,
160+
externs: [
161+
'node_modules/firebase/externs/firebase-app-externs.js',
162+
'node_modules/firebase/externs/firebase-auth-externs.js',
163+
'node_modules/firebase/externs/firebase-client-auth-externs.js'
164+
],
165+
only_closure_dependencies: true,
166+
output_wrapper: OUTPUT_WRAPPER,
167+
168+
// This is required to match XTB IDs to the JS/Soy messages.
169+
translations_project: 'FirebaseUI'
170+
};
171+
if (locale !== DEFAULT_LOCALE) {
172+
flags.translations_file = `translations/${locale}.xtb`;
173+
}
174+
return compile([
175+
'node_modules/google-closure-templates/javascript/soyutils_usegoog.js',
176+
'node_modules/google-closure-library/closure/goog/**/*.js',
177+
'node_modules/google-closure-library/third_party/closure/goog/**/*.js',
178+
`${TMP_DIR}/**/*.js`,
179+
'javascript/**/*.js'
180+
], getTmpJsPath(locale), flags);
181+
}
63182

64183
// Builds the core FirebaseUI JS.
65-
gulp.task('build-firebaseui-js', () =>
66-
gulp
67-
.src([
68-
'node_modules/google-closure-templates/javascript/soyutils_usegoog.js',
69-
'node_modules/google-closure-library/closure/goog/**/*.js',
70-
'node_modules/google-closure-library/third_party/closure/goog/**/*.js',
71-
'out/**/*.js',
72-
'javascript/**/*.js'
73-
])
74-
.pipe(closureCompiler({
75-
compilerPath: COMPILER_PATH,
76-
fileName: 'firebaseui.js',
77-
compilerFlags: {
78-
closure_entry_point: 'firebaseui.auth.exports',
79-
compilation_level: OPTIMIZATION_LEVEL,
80-
externs: [
81-
'node_modules/firebase/externs/firebase-app-externs.js',
82-
'node_modules/firebase/externs/firebase-auth-externs.js',
83-
'node_modules/firebase/externs/firebase-client-auth-externs.js'
84-
],
85-
language_out: 'ES5',
86-
only_closure_dependencies: true,
87-
output_wrapper: OUTPUT_WRAPPER
88-
}
89-
}))
90-
.pipe(gulp.dest('out')));
91-
92-
93-
// Bundles the FirebaseUI JS with its dependencies.
94-
gulp.task('build-js', ['build-firebaseui-js'], () =>
95-
gulp
96-
.src([
97-
'node_modules/material-design-lite/src/mdlComponentHandler.js',
98-
'node_modules/material-design-lite/src/button/button.js',
99-
'node_modules/material-design-lite/src/progress/progress.js',
100-
'node_modules/material-design-lite/src/spinner/spinner.js',
101-
'node_modules/material-design-lite/src/textfield/textfield.js',
102-
'node_modules/dialog-polyfill/dialog-polyfill.js',
103-
'out/firebaseui.js'
104-
])
105-
.pipe(concatJS('firebaseui.js', OUTPUT_WRAPPER))
106-
.pipe(gulp.dest('dist')));
184+
repeatTaskForAllLocales('build-firebaseui-js-$', ['build-soy'],
185+
buildFirebaseUiJs);
186+
187+
// The external dependencies needed by FirebaseUI.
188+
const JS_DEPS = [
189+
'node_modules/material-design-lite/src/mdlComponentHandler.js',
190+
'node_modules/material-design-lite/src/button/button.js',
191+
'node_modules/material-design-lite/src/progress/progress.js',
192+
'node_modules/material-design-lite/src/spinner/spinner.js',
193+
'node_modules/material-design-lite/src/textfield/textfield.js',
194+
'node_modules/dialog-polyfill/dialog-polyfill.js'
195+
];
196+
197+
/**
198+
* Creates the default FirebaseUI binaries for basic usage without
199+
* localization. For example, it copies firebaseui__en.js to firebaseui.js.
200+
* @param {string} fileName
201+
*/
202+
function makeDefaultFile(fileName) {
203+
const localeForFileName = getLocaleForFileName(DEFAULT_LOCALE);
204+
const path = `${DEST_DIR}/${fileName}__${localeForFileName}.js`;
205+
if (fse.existsSync(path)) {
206+
return fse.copy(path, `${DEST_DIR}/${fileName}.js`);
207+
}
208+
}
209+
210+
// Bundles the FirebaseUI JS with its dependencies for all locales.
211+
const buildJsTasks = repeatTaskForAllLocales('build-js-$',
212+
['build-firebaseui-js-$'], (locale) => {
213+
const localeForFileName = getLocaleForFileName(locale);
214+
return compile(
215+
JS_DEPS.concat([getTmpJsPath(locale)]),
216+
`${DEST_DIR}/firebaseui__${localeForFileName}.js`, {
217+
compilation_level: 'WHITESPACE_ONLY',
218+
output_wrapper: OUTPUT_WRAPPER
219+
});
220+
});
221+
gulp.task('build-all-js', buildJsTasks, () => makeDefaultFile('firebaseui'));
222+
gulp.task('build-js', ['build-js-' + DEFAULT_LOCALE],
223+
() => makeDefaultFile('firebaseui'));
107224

108225
// Bundles the FirebaseUI JS with its dependencies as a NPM module.
109-
gulp.task('build-npm', ['build-firebaseui-js'], () =>
110-
gulp
111-
.src([
112-
'node_modules/material-design-lite/src/mdlComponentHandler.js',
113-
'node_modules/material-design-lite/src/button/button.js',
114-
'node_modules/material-design-lite/src/progress/progress.js',
115-
'node_modules/material-design-lite/src/spinner/spinner.js',
116-
'node_modules/material-design-lite/src/textfield/textfield.js',
117-
'node_modules/dialog-polyfill/dialog-polyfill.js',
118-
'out/firebaseui.js'
119-
])
120-
.pipe(concatJS('npm.js', NPM_MODULE_WRAPPER))
121-
.pipe(gulp.dest('dist')));
226+
repeatTaskForAllLocales('build-npm-$', ['build-firebaseui-js-$'], (locale) => {
227+
const localeForFileName = getLocaleForFileName(locale);
228+
const srcs = JS_DEPS.concat([getTmpJsPath(locale)]);
229+
return compile(srcs, `dist/npm__${localeForFileName}.js`, {
230+
compilation_level: 'WHITESPACE_ONLY',
231+
output_wrapper: NPM_MODULE_WRAPPER
232+
});
233+
});
234+
gulp.task('build-npm', ['build-npm-' + DEFAULT_LOCALE],
235+
() => makeDefaultFile('npm'));
122236

123-
// Concatenates and minifies the CSS sources.
124-
gulp.task('build-css', () => {
125-
var mdlSrcs = gulp.src('stylesheet/mdl.scss')
237+
/**
238+
* Builds the CSS for FirebaseUI.
239+
* @param {boolean} isRtl Whether to build in right-to-left mode.
240+
* @return {*} A stream that finishes when compilation finishes.
241+
*/
242+
function buildCss(isRtl) {
243+
const mdlSrcs = gulp.src('stylesheet/mdl.scss')
126244
.pipe(sass.sync().on('error', sass.logError));
127-
var dialogPolyfillSrcs = gulp.src(
245+
const dialogPolyfillSrcs = gulp.src(
128246
'node_modules/dialog-polyfill/dialog-polyfill.css');
129-
var firebaseSrcs = gulp.src('stylesheet/*.css');
247+
let firebaseSrcs = gulp.src('stylesheet/*.css');
248+
if (isRtl) {
249+
firebaseSrcs = firebaseSrcs.pipe(flip.gulp());
250+
}
130251

252+
const outFile = isRtl ? 'firebaseui-rtl.css' : 'firebaseui.css';
131253
return streamqueue({objectMode: true},
132254
mdlSrcs, dialogPolyfillSrcs, firebaseSrcs)
133-
.pipe(concatCSS('firebaseui.css'))
255+
.pipe(concatCSS(outFile))
134256
.pipe(cleanCSS())
135257
.pipe(gulp.dest('dist'));
136-
});
258+
}
259+
260+
// Concatenates and minifies the CSS sources.
261+
gulp.task('build-css', () => buildCss(false));
262+
gulp.task('build-css-rtl', () => buildCss(true));
137263

138264
// Creates a webserver that serves all files from the root of the package.
139265
gulp.task('serve', () => {
140-
var connect = require('gulp-connect');
141-
142266
connect.server({
143267
port: 4000
144268
});
145269
});
146270

147-
148271
// Deletes intermediate files.
149-
gulp.task('clean', () => del(['out/*', 'out']));
272+
gulp.task('clean', () => fse.remove(TMP_DIR));
150273

274+
// Executes the basic tasks.
275+
gulp.task('default', ['build-js', 'build-npm', 'build-css', 'build-css-rtl'],
276+
() => gulp.start('clean'));
151277

152-
gulp.task(
153-
'default',
154-
['build-js', 'build-npm', 'build-css'],
278+
// Builds everything.
279+
gulp.task('build-all',
280+
['build-all-js', 'build-npm', 'build-css', 'build-css-rtl'],
155281
() => gulp.start('clean'));

0 commit comments

Comments
 (0)