|
12 | 12 | * limitations under the License.
|
13 | 13 | */
|
14 | 14 |
|
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'); |
22 | 27 |
|
| 28 | +const glob = closureBuilder.globSupport(); |
23 | 29 |
|
24 | 30 | // The optimization level for the JS compiler.
|
25 | 31 | // 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'; |
29 | 36 |
|
30 | 37 | // 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' ? |
32 | 39 | '%output%' : '(function() { %output% })();';
|
33 | 40 |
|
34 | 41 | // 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' ? |
36 | 43 | 'var firebase=require(\'firebase\');%output%module.exports=firebaseui;' :
|
37 | 44 | '(function() { var firebase=require(\'firebase\');%output% })();' +
|
38 | 45 | 'module.exports=firebaseui;';
|
39 | 46 |
|
40 |
| - |
41 | 47 | // 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']; |
43 | 65 |
|
| 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 | +} |
44 | 108 |
|
45 | 109 | /**
|
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. |
50 | 113 | */
|
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)); |
60 | 148 | });
|
| 149 | + return tasks; |
61 | 150 | }
|
62 | 151 |
|
| 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 | +} |
63 | 182 |
|
64 | 183 | // 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')); |
107 | 224 |
|
108 | 225 | // 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')); |
122 | 236 |
|
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') |
126 | 244 | .pipe(sass.sync().on('error', sass.logError));
|
127 |
| - var dialogPolyfillSrcs = gulp.src( |
| 245 | + const dialogPolyfillSrcs = gulp.src( |
128 | 246 | '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 | + } |
130 | 251 |
|
| 252 | + const outFile = isRtl ? 'firebaseui-rtl.css' : 'firebaseui.css'; |
131 | 253 | return streamqueue({objectMode: true},
|
132 | 254 | mdlSrcs, dialogPolyfillSrcs, firebaseSrcs)
|
133 |
| - .pipe(concatCSS('firebaseui.css')) |
| 255 | + .pipe(concatCSS(outFile)) |
134 | 256 | .pipe(cleanCSS())
|
135 | 257 | .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)); |
137 | 263 |
|
138 | 264 | // Creates a webserver that serves all files from the root of the package.
|
139 | 265 | gulp.task('serve', () => {
|
140 |
| - var connect = require('gulp-connect'); |
141 |
| - |
142 | 266 | connect.server({
|
143 | 267 | port: 4000
|
144 | 268 | });
|
145 | 269 | });
|
146 | 270 |
|
147 |
| - |
148 | 271 | // Deletes intermediate files.
|
149 |
| -gulp.task('clean', () => del(['out/*', 'out'])); |
| 272 | +gulp.task('clean', () => fse.remove(TMP_DIR)); |
150 | 273 |
|
| 274 | +// Executes the basic tasks. |
| 275 | +gulp.task('default', ['build-js', 'build-npm', 'build-css', 'build-css-rtl'], |
| 276 | + () => gulp.start('clean')); |
151 | 277 |
|
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'], |
155 | 281 | () => gulp.start('clean'));
|
0 commit comments