build.js 12.4 KB
Newer Older
C
Christoph Held 已提交
1
// Copyright 2017 The Kubernetes Authors.
2 3 4 5 6
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
B
bryk 已提交
7
//     http://www.apache.org/licenses/LICENSE-2.0
8 9 10 11 12 13 14
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

C
Christoph Held 已提交
15

16 17 18 19 20
/**
 * @fileoverview Gulp tasks for building the project.
 */
import del from 'del';
import gulp from 'gulp';
21
import gulpUrlAdjuster from 'gulp-css-url-adjuster';
B
bryk 已提交
22
import gulpHtmlmin from 'gulp-htmlmin';
23
import gulpIf from 'gulp-if';
24
import gulpMinifyCss from 'gulp-minify-css';
25
import revAll from 'gulp-rev-all';
26 27
import gulpUglify from 'gulp-uglify';
import gulpUseref from 'gulp-useref';
28
import mergeStream from 'merge-stream';
29
import path from 'path';
30
import uglifySaveLicense from 'uglify-save-license';
31 32

import conf from './conf';
B
bryk 已提交
33
import {multiDest} from './multidest';
34

35

36
/**
B
bryk 已提交
37
 * Builds production package for current architecture and places it in the dist directory.
38
 */
39 40
gulp.task('build', ['backend:prod', 'build-frontend']);

B
bryk 已提交
41
/**
W
wxb0521 已提交
42
 * Builds production packages for all supported architectures and places them in the dist directory.
B
bryk 已提交
43 44 45
 */
gulp.task('build:cross', ['backend:prod:cross', 'build-frontend:cross']);

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

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

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

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

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

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

B
bryk 已提交
91
/**
92
 * Builds production version of the frontend application for the default architecture
W
wxb0521 已提交
93
 * (one copy per locale) and places it under .tmp/dist , preparing it for localization and revision.
B
bryk 已提交
94
 */
95 96 97 98 99
gulp.task(
    'frontend-copies',
    ['fonts', 'icons', 'assets', 'dependency-images', 'index:prod', 'clean-dist'], function() {
      return createFrontendCopies([path.join(conf.paths.distPre, conf.arch.default, 'public')]);
    });
B
bryk 已提交
100 101

/**
W
wxb0521 已提交
102
 * Builds production versions of the frontend application for all architectures
103
 * (one copy per locale) and places them under .tmp, preparing them for localization and revision.
B
bryk 已提交
104
 */
105 106 107 108 109 110 111 112 113 114 115 116 117 118
gulp.task(
    'frontend-copies:cross',
    [
      'fonts:cross',
      'icons:cross',
      'assets:cross',
      'dependency-images:cross',
      'index:prod',
      'clean-dist',
    ],
    function() {
      return createFrontendCopies(
          conf.arch.list.map((arch) => path.join(conf.paths.distPre, arch, 'public')));
    });
B
bryk 已提交
119 120 121 122

/**
 * Copies assets to the dist directory for current architecture.
 */
123 124 125
gulp.task('assets', ['clean-dist'], function() {
  return assets([conf.paths.distPublic]);
});
B
bryk 已提交
126 127 128 129

/**
 * Copies assets to the dist directory for all architectures.
 */
130 131 132
gulp.task('assets:cross', ['clean-dist'], function() {
  return assets(conf.paths.distPublicCross);
});
B
bryk 已提交
133

134 135 136
/**
 * Copies icons to the dist directory for current architecture.
 */
137 138 139
gulp.task('icons', ['clean-dist'], function() {
  return icons([conf.paths.distPublic]);
});
140 141 142 143

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

/**
 * Copies fonts to the dist directory for current architecture.
 */
151 152 153
gulp.task('fonts', ['clean-dist'], function() {
  return fonts([conf.paths.distPublic]);
});
154 155 156 157

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

162 163 164
/**
 * Copies images from dependencies to the dist directory for current architecture.
 */
165 166 167
gulp.task('dependency-images', ['clean-dist'], function() {
  return dependencyImages([conf.paths.distPublic]);
});
168 169 170 171

/**
 * Copies images from dependencies to the dist directory for all architectures.
 */
172 173 174
gulp.task('dependency-images:cross', ['clean-dist'], function() {
  return dependencyImages(conf.paths.distPublicCross);
});
175

B
bryk 已提交
176 177 178 179 180 181 182
/**
 * Cleans all build artifacts.
 */
gulp.task('clean', ['clean-dist'], function() {
  return del([conf.paths.goWorkspace, conf.paths.tmp, conf.paths.coverage]);
});

183 184 185 186 187 188 189
/**
 * Cleans all message for extraction files.
 */
gulp.task('clean-messages-for-extraction', [], function() {
  return del([conf.paths.messagesForExtraction]);
});

B
bryk 已提交
190 191 192
/**
 * Cleans all build artifacts in the dist/ folder.
 */
193 194 195
gulp.task('clean-dist', function() {
  return del([conf.paths.distRoot, conf.paths.distPre]);
});
B
bryk 已提交
196

197
/**
198 199
 * Builds production version of the frontend application and copies it to all
 * the specified outputDirs, creating one copy per (outputDir x locale) tuple.
200 201 202 203
 *
 * Following steps are done here:
 *  1. Vendor CSS and JS files are concatenated and minified.
 *  2. index.html is minified.
204 205
 *  3. Everything is saved in the .tmp/dist directory, ready to be localized and revisioned.
 *
206
 * @param {!Array<string>} outputDirs
B
bryk 已提交
207
 * @return {stream}
208
 */
209
function createFrontendCopies(outputDirs) {
210 211
  // create an output for each locale
  let localizedOutputDirs = outputDirs.reduce((localizedDirs, outputDir) => {
212 213 214
    return localizedDirs.concat(conf.translations.map((translation) => {
      return path.join(outputDir, translation.key);
    }));
215 216
  }, []);

B
bryk 已提交
217 218 219
  let searchPath = [
    // To resolve local paths.
    path.relative(conf.paths.base, conf.paths.prodTmp),
220
    // To resolve node_modules/... paths.
B
bryk 已提交
221 222
    path.relative(conf.paths.base, conf.paths.base),
  ];
223 224

  return gulp.src(path.join(conf.paths.prodTmp, '*.html'))
B
bryk 已提交
225
      .pipe(gulpUseref({searchPath: searchPath}))
226 227 228 229 230 231 232
      .pipe(gulpIf(
          '**/vendor.css',
          gulpMinifyCss({rebase: true, relativeTo: conf.paths.tmp, target: conf.paths.tmp})))
      .pipe(gulpIf('**/vendor.css', gulpUrlAdjuster({
                     // Replace invalid prefix that is added to resolved URLs.
                     replace: ['prod/static/', ''],
                   })))
233 234 235 236
      .pipe(gulpIf('**/vendor.css', gulpUrlAdjuster({
                     // Replace invalid prefix that is added to resolved URLs.
                     replace: ['prod/', ''],
                   })))
237
      .pipe(gulpIf('**/vendor.js', gulpUglify({
238 239 240 241
                     output: {
                       comments: uglifySaveLicense,
                     },
                     // preserveComments: uglifySaveLicense,
242 243 244 245 246
                     // Disable compression of unused vars. This speeds up minification a lot (like
                     // 10 times).
                     // See https://github.com/mishoo/UglifyJS2/issues/321
                     compress: {unused: false},
                   })))
247 248 249 250 251
      .pipe(gulpIf('*.html', gulpHtmlmin({
                     removeComments: true,
                     collapseWhitespace: true,
                     conservativeCollapse: true,
                   })))
252 253 254 255 256
      .pipe(multiDest(localizedOutputDirs));
}

/**
 * Creates revisions of all .js anc .css files at once (for production).
sailing666's avatar
sailing666 已提交
257
 * Replaces the occurences of those files in index.html with their new names.
258 259 260 261 262
 * index.html does not get renamed in the process.
 * The processed files are then moved to the dist directory.
 * @return {stream}
 */
function doRevision() {
263 264 265 266 267
  return gulp
      .src([path.join(conf.paths.distPre, '**'), '!**/assets/**/*'])
      // Do not update references other than in index.html. Do not rev index.html itself.
      .pipe(revAll.revision(
          {dontRenameFile: ['index.html'], dontSearchFile: [/^(?!.*index\.html$).*$/]}))
268 269 270 271
      .pipe(gulp.dest(conf.paths.distRoot));
}

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

  return mergeStream.apply(null, streams);
B
bryk 已提交
287
}
288 289

/**
290 291 292 293 294 295 296 297 298 299 300 301 302
 * 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
B
bryk 已提交
303
 * @return {stream}
304
 */
B
bryk 已提交
305
function assets(outputDirs) {
306
  let localizedOutputDirs = createLocalizedOutputs(outputDirs);
307
  return gulp.src(path.join(conf.paths.assets, '/**/*'), {base: conf.paths.app})
308
      .pipe(multiDest(localizedOutputDirs));
B
bryk 已提交
309
}
310 311

/**
312 313
 * Copies the icons files to all dist directories per arch and locale.
 * @param {!Array<string>} outputDirs
314 315 316
 * @return {stream}
 */
function icons(outputDirs) {
317
  let localizedOutputDirs = createLocalizedOutputs(outputDirs, 'static/');
318 319 320 321
  return gulp
      .src(
          path.join(conf.paths.materialIcons, '/**/*.+(woff2|woff|eot|ttf)'),
          {base: conf.paths.materialIcons})
322
      .pipe(multiDest(localizedOutputDirs));
323 324 325
}

/**
326 327
 * Copies the font files to all dist directories per arch and locale.
 * @param {!Array<string>} outputDirs
328 329 330
 * @return {stream}
 */
function fonts(outputDirs) {
331
  let localizedOutputDirs = createLocalizedOutputs(outputDirs, 'static/');
332 333 334 335 336 337 338 339 340 341 342

  let roboto =
      gulp.src(path.join(conf.paths.robotoFonts, '/**/*.*'), {base: conf.paths.robotoFontsBase})
          .pipe(multiDest(localizedOutputDirs));

  let robotoMono = gulp.src(
                           path.join(conf.paths.robotoMonoFonts, '/**/*.*'),
                           {base: conf.paths.robotoMonoFontsBase})
                       .pipe(multiDest(localizedOutputDirs));

  return mergeStream.apply(null, [roboto, robotoMono]);
343
}
344

345 346 347 348 349 350 351
/**
 * Copies the font files to all dist directories per arch and locale.
 * @param {!Array<string>} outputDirs
 * @return {stream}
 */
function dependencyImages(outputDirs) {
  let localizedOutputDirs = createLocalizedOutputs(outputDirs, 'static/img');
352 353
  return gulp
      .src(path.join(conf.paths.jsoneditorImages, '*.png'), {base: conf.paths.jsoneditorImages})
354 355 356
      .pipe(multiDest(localizedOutputDirs));
}

357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
/**
 * 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);
    }));
  }, []);
}