Skip to content
Snippets Groups Projects
Select Git revision
  • edab830346cd12f58164539fc377d71acb2feb3c
  • main default protected
  • release/1.1
  • encrypt_comments
  • mnemonic_dewif
  • authors_rules
  • 0.14
  • rtd
  • 1.2.1 protected
  • 1.2.0 protected
  • 1.1.1 protected
  • 1.1.0 protected
  • 1.0.0 protected
  • 1.0.0rc1 protected
  • 1.0.0rc0 protected
  • 1.0.0-rc protected
  • 0.62.0 protected
  • 0.61.0 protected
  • 0.60.1 protected
  • 0.58.1 protected
  • 0.60.0 protected
  • 0.58.0 protected
  • 0.57.0 protected
  • 0.56.0 protected
  • 0.55.1 protected
  • 0.55.0 protected
  • 0.54.3 protected
  • 0.54.2 protected
28 results

Makefile

Blame
  • gulpfile.js 43.29 KiB
    'use strict';
    
    const gulp = require('gulp'),
      path = require("path"),
      sass = require('gulp-sass'),
      cleanCss = require('gulp-clean-css'),
      base64 = require('gulp-base64-v2'),
      rename = require('gulp-rename'),
      ngConstant = require('gulp-ng-constant'),
      fs = require("fs"),
      header = require('gulp-header'),
      footer = require('gulp-footer'),
      removeCode = require('gulp-remove-code'),
      removeHtml = require('gulp-html-remove'),
      templateCache = require('gulp-angular-templatecache'),
      ngTranslate = require('gulp-angular-translate'),
      ngAnnotate = require('gulp-ng-annotate'),
      zip = require('gulp-zip'),
      del = require('del'),
      debug = require('gulp-debug'),
      useref = require('gulp-useref'),
      filter = require('gulp-filter'),
      uglify = require('gulp-uglify-es').default,
      sourcemaps = require('gulp-sourcemaps'),
      lazypipe = require('lazypipe'),
      csso = require('gulp-csso'),
      replace = require('gulp-replace'),
      clean = require('gulp-clean'),
      htmlmin = require('gulp-htmlmin'),
      jshint = require('gulp-jshint'),
      markdown = require('gulp-markdown'),
      merge = require('merge2'),
      log = require('fancy-log'),
      colors = require('ansi-colors'),
      argv = require('yargs').argv,
      sriHash = require('gulp-sri-hash'),
      sort = require('gulp-sort'),
      map = require('map-stream');
    
      // Workaround because @ioni/v1-toolkit use gulp v3.9.2 instead of gulp v4
      let jsonlint;
      try {
        jsonlint = require('@prantlf/gulp-jsonlint');
      } catch(e) {
        log(colors.red("Cannot load 'gulp-jsonlint'. Retrying using project path"), e);
      }
    
    
    const paths = {
      license_md: ['./www/license/*.md'],
      sass: ['./scss/ionic.app.scss'],
      config: ['./app/config.json'],
      templatecache: ['./www/templates/**/*.html'],
      ng_translate: ['./www/i18n/locale-*.json'],
      ng_annotate: ['./www/js/**/*.js', '!./www/js/vendor/*.js'],
      // plugins:
      leafletSass: ['./scss/leaflet.app.scss'],
      css_plugin: ['./www/plugins/*/css/**/*.css'],
      templatecache_plugin: ['./www/plugins/*/templates/**/*.html'],
      ng_translate_plugin: ['./www/plugins/*/i18n/locale-*.json'],
      ng_annotate_plugin: ['./www/plugins/*/**/*.js', '!./www/plugins/*/js/vendor/*.js']
    };
    
    const uglifyBaseOptions = {
      toplevel: true,
      warnings: true,
      mangle: {
        reserved: ['qrcode', 'Base58']
      },
      compress: {
        global_defs: {
          "@console.log": "alert"
        },
        passes: 2
      },
      output: {
        beautify: false,
        preamble: "/* minified */",
        max_line_len: 120000
      }
    };
    const debugBaseOptions = {
      title: 'Processing',
      minimal: true,
      showFiles: argv.debug || false,
      showCount: argv.debug || false,
      logger: m => log(colors.grey(m))
    };
    
    function appAndPluginWatch(done) {
    
      log(colors.green('Watching source files...'));
    
      // Licenses
      gulp.watch(paths.license_md, () => appLicense());
    
      // App
      gulp.watch(paths.sass, () => appSass());
      gulp.watch(paths.templatecache, () => appNgTemplate());
      gulp.watch(paths.ng_annotate, (event) => appNgAnnotate(event));
      gulp.watch(paths.ng_translate, () => appNgTranslate());
      // Plugins
      gulp.watch(paths.templatecache_plugin, () => pluginNgTemplate());
      gulp.watch(paths.ng_annotate_plugin, (event) => pluginNgAnnotate(event));
      gulp.watch(paths.ng_translate_plugin, () => pluginNgTranslate());
      gulp.watch(paths.css_plugin.concat(paths.leafletSass), () => pluginSass());
    
      if (done) done();
    }
    
    
    function appAndPluginClean() {
      return del([
        './www/dist',
        './www/css/ionic.app*.css',
        './www/css/leaflet.app*.css'
      ]);
    }
    
    /**
     * Generate App CSS (using SASS)
     * @returns {*}
     */
    function appSass() {
      log(colors.green('Building App Sass...'));
    
      // Default App style
      return gulp.src('./scss/ionic.app.scss')
    
        .pipe(sass()).on('error', sass.logError)
        .pipe(base64({
          baseDir: "./www/css",
          extensions: ['svg', 'png', 'gif', /\.jpg#datauri$/i],
          maxImageSize: 14 * 1024
        }))
        .pipe(gulp.dest('./www/css/'))
        .pipe(cleanCss({
          keepSpecialComments: 0
        }))
        .pipe(rename({ extname: '.min.css' }))
        .pipe(gulp.dest('./www/css/'));
    }
    
    function appConfig() {
      const allConfig = JSON.parse(fs.readFileSync('./app/config.json', 'utf8'));
    
      // Determine which environment to use when building config.
      const env = argv.env || 'default';
      const config = allConfig[env];
    
      if (!config) {
        throw new Error(colors.red("=> Could not load `" + env + "` environment!"));
      }
    
      log(colors.green("Building App config at `www/js/config.js` for `" + env + "` environment..."));
    
      const project = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
      config['version'] = project.version;
      config['build'] = (new Date()).toJSON();
      config['newIssueUrl'] = project.bugs.new;
    
      return ngConstant({
        name: 'cesium.config',
        constants: {"csConfig": config},
        stream: true,
        dest: 'config.js'
      })
        // Add a warning header
        .pipe(header("/******\n* !! WARNING: This is a generated file !!\n*\n* PLEASE DO NOT MODIFY DIRECTLY\n*\n* => Changes should be done on file 'app/config.json'.\n******/\n\n"))
        // Writes into file www/js/config.js
        .pipe(rename('config.js'))
        .pipe(gulp.dest('www/js'));
    }
    
    function appAndPluginLint(done) {
      log(colors.green('Linting JS files...'));
    
      // Copy Js (and remove unused code)
      return gulp.src(paths.ng_annotate.concat(paths.ng_annotate_plugin))
        .pipe(debug({...debugBaseOptions, title: 'Linting'}))
        .pipe(jshint())
        .pipe(jshint.reporter(require('jshint-stylish')))
        .pipe(map( (file, cb) => {
          if (!file.jshint.success) {
            console.error('jshint failed');
            process.exit(1);
          }
        }))
        .on('end', done);
    }
    
    function appNgTemplate() {
      log(colors.green('Building template file...'));
    
      return gulp.src(paths.templatecache)
        .pipe(sort())
        .pipe(templateCache({
          standalone: true,
          module: "cesium.templates",
          root: "templates/"
        }))
        .pipe(gulp.dest('./www/dist/dist_js/app'));
    }
    
    function appNgAnnotate(event) {
    
      // If watch, apply only on changes files
      if (event && event.type === 'changed' && event.path && event.path.indexOf('/www/js/') !== -1) {
        let path = event.path.substring(event.path.indexOf('/www/js') + 7);
        path = path.substring(0, path.lastIndexOf('/'));
        return gulp.src(event.path)
          .pipe(ngAnnotate({single_quotes: true}))
          .pipe(gulp.dest('./www/dist/dist_js/app' + path));
      }
    
      log(colors.green('Building JS files...'));
      return gulp.src(paths.ng_annotate)
        .pipe(ngAnnotate({single_quotes: true}))
        .pipe(gulp.dest('./www/dist/dist_js/app'));
    }
    
    function appNgTranslate() {
      log(colors.green('Building translation file...'));
    
      return gulp.src('www/i18n/locale-*.json')
        .pipe(jsonlint())
        .pipe(jsonlint.reporter())
        .pipe(sort())
        .pipe(ngTranslate({standalone:true, module: 'cesium.translations'}))
        .pipe(gulp.dest('www/dist/dist_js/app'));
    }
    
    function appLicense() {
      log(colors.green('Building License files...'));
    
      return merge(
        // Copy license into HTML
        gulp.src(paths.license_md)
          .pipe(markdown())
          .pipe(header('<html><header><meta charset="utf-8"></header><body>'))
          .pipe(footer('</body></html>'))
          .pipe(gulp.dest('www/license')),
    
        // Copy license into txt
        gulp.src(paths.license_md)
          .pipe(header('\ufeff')) // Need BOM character for UTF-8 files
          .pipe(rename({ extname: '.txt' }))
          .pipe(gulp.dest('www/license'))
      );
    }
    
    /* -- Plugins -- */
    
    function pluginNgTemplate() {
      log(colors.green('Building Plugins template file...'));
    
      return gulp.src(paths.templatecache_plugin)
        .pipe(sort())
        .pipe(templateCache({
          standalone: true,
          module: "cesium.plugins.templates",
          root: "plugins/"
        }))
        .pipe(gulp.dest('./www/dist/dist_js/plugins'));
    }
    
    function pluginNgAnnotate(event) {
    
      // If watch, apply only on changes files
      if (event && event.type === 'changed' && event.path && event.path.indexOf('/www/js/') !== -1) {
        let path = event.path.substring(event.path.indexOf('/www/js') + 7);
        path = path.substring(0, path.lastIndexOf('/'));
        return gulp.src(event.path)
          .pipe(ngAnnotate({single_quotes: true}))
          .pipe(gulp.dest('./www/dist/dist_js/app' + path));
      }
    
      log(colors.green('Building Plugins JS file...'));
      return gulp.src(paths.ng_annotate_plugin)
        .pipe(ngAnnotate({single_quotes: true}))
        .pipe(gulp.dest('./www/dist/dist_js/plugins'));
    }
    
    function pluginNgTranslate() {
      log(colors.green('Building Plugins translation file...'));
    
      return gulp.src(paths.ng_translate_plugin)
        .pipe(jsonlint())
        .pipe(jsonlint.reporter())
        .pipe(sort())
        .pipe(ngTranslate({standalone:true, module: 'cesium.plugins.translations'}))
        .pipe(gulp.dest('www/dist/dist_js/plugins'));
    }
    
    function pluginLeafletImages(dest) {
      dest = dest || './www/img/';
      // Leaflet images
      return gulp.src(['scss/leaflet/images/*.*',
        'www/lib/leaflet/dist/images/*.*',
        'www/lib/leaflet-search/images/*.*',
        '!www/lib/leaflet-search/images/back.png',
        '!www/lib/leaflet-search/images/leaflet-search.jpg',
        'www/lib/leaflet.awesome-markers/dist/images/*.*'],
          {read: false, allowEmpty: true}
        )
        .pipe(gulp.dest(dest));
    }
    
    function pluginSass() {
      log(colors.green('Building Plugins Sass...'));
    
      return merge(
    
        // Copy plugins CSS
        gulp.src(paths.css_plugin)
          .pipe(gulp.dest('www/dist/dist_css/plugins')),
    
        // Leaflet images
        pluginLeafletImages('./www/img/'),
    
        // Leaflet App style
        gulp.src('./scss/leaflet.app.scss')
          .pipe(sass()).on('error', sass.logError)
          // Fix bad images path
          .pipe(replace("url('../images/", "url('../img/"))
          .pipe(replace("url(\"../images/", "url(\"../img/"))
          .pipe(replace("url('images/", "url('../img/"))
          .pipe(replace("url(\"images/", "url(\"../img/"))
          .pipe(replace("url(images/", "url(../img/"))
          .pipe(base64({
            baseDir: "./www/css/",
            extensions: ['png', 'gif', /\.jpg#datauri$/i],
            maxImageSize: 14 * 1024,
            deleteAfterEncoding: false
          }))
          .pipe(gulp.dest('./www/css/'))
          .pipe(cleanCss({
            keepSpecialComments: 0
          }))
          .pipe(rename({ extname: '.min.css' }))
          .pipe(gulp.dest('./www/css/'))
      );
    }
    
    /* --------------------------------------------------------------------------
       -- Build the web (ZIP) artifact
       --------------------------------------------------------------------------*/
    
    function webClean() {
      return del([
        './dist/web/www'
      ]);
    }
    
    function webCopyFiles() {
      log(colors.green('Preparing dist/web files...'));
      let htmlminOptions = {removeComments: true, collapseWhitespace: true};
    
      const debugOptions = { ...debugBaseOptions, title: 'Copying' };
    
      var targetPath = './dist/web/www';
      return merge(
        // Copy Js (and remove unused code)
        gulp.src('./www/js/**/*.js')
          .pipe(debug(debugOptions))
          .pipe(removeCode({"no-device": true}))
          .pipe(jshint())
          .pipe(gulp.dest(targetPath + '/js')),
    
        // Copy HTML templates (and remove unused code)
        gulp.src('./www/templates/**/*.html')
          .pipe(removeCode({"no-device": true}))
          .pipe(removeHtml('.hidden-no-device'))
          .pipe(removeHtml('[remove-if][remove-if="no-device"]'))
          .pipe(htmlmin(htmlminOptions))
          .pipe(gulp.dest(targetPath + '/templates')),
    
        // Copy index.html (and remove unused code)
        gulp.src('./www/index.html')
          .pipe(removeCode({'no-device': true}))
          .pipe(removeHtml('.hidden-no-device'))
          .pipe(removeHtml('[remove-if][remove-if="no-device"]'))
          .pipe(htmlmin(/*no options, to keep comments*/))
          .pipe(gulp.dest(targetPath)),
    
        // Copy API index.html
        gulp.src('./www/api/index.html')
          .pipe(removeCode({'no-device': true}))
          .pipe(removeHtml('.hidden-no-device'))
          .pipe(removeHtml('[remove-if][remove-if="no-device"]'))
          .pipe(htmlmin())
          .pipe(gulp.dest(targetPath + '/api')),
    
        // Copy fonts
        gulp.src('./www/fonts/**/*.*')
          .pipe(debug(debugOptions))
          .pipe(gulp.dest(targetPath + '/fonts')),
    
        // Copy CSS
        gulp.src('./www/css/**/*.*')
          .pipe(debug(debugOptions))
          .pipe(gulp.dest(targetPath + '/css')),
    
        // Copy i18n
        gulp.src('./www/i18n/locale-*.json')
          .pipe(jsonlint())
          .pipe(jsonlint.reporter())
          .pipe(sort())
          .pipe(ngTranslate({standalone:true, module: 'cesium.translations'}))
          .pipe(debug(debugOptions))
          .pipe(gulp.dest(targetPath + '/js')),
    
        // Copy img
        gulp.src('./www/img/**/*.*')
          .pipe(debug(debugOptions))
          .pipe(gulp.dest(targetPath + '/img')),
    
        // Copy manifest.json
        gulp.src('./www/manifest.json')
          .pipe(debug(debugOptions))
          .pipe(gulp.dest(targetPath)),
    
        // Copy lib (JS, CSS and fonts)
        gulp.src(['./www/lib/**/*.js', './www/lib/**/*.css', './www/lib/**/fonts/**/*.*'])
          .pipe(debug(debugOptions))
          .pipe(gulp.dest(targetPath + '/lib')),
    
        // Copy license into HTML
        gulp.src('./www/license/*.md')
          .pipe(markdown())
          .pipe(header('<html><header><meta charset="utf-8"></header><body>'))
          .pipe(footer('</body></html>'))
          .pipe(gulp.dest(targetPath + '/license')),
    
        // Copy license into txt
        gulp.src('./www/license/*.md')
          .pipe(header('\ufeff')) // Need BOM character for UTF-8 files
          .pipe(rename({ extname: '.txt' }))
          .pipe(gulp.dest(targetPath + '/license'))
      );
    }
    
    function webNgTemplate() {
      var targetPath = './dist/web/www';
      return gulp.src(targetPath + '/templates/**/*.html')
        .pipe(sort())
        .pipe(templateCache({
          standalone: true,
          module: "cesium.templates",
          root: "templates/"
        }))
        .pipe(gulp.dest(targetPath + '/dist/dist_js/app'));
    }
    
    function webAppNgAnnotate() {
      var targetPath = './dist/web/www';
      var jsFilter = filter(["**/*.js", "!**/vendor/*"]);
    
      return gulp.src(targetPath + '/js/**/*.js')
        .pipe(jsFilter)
        .pipe(ngAnnotate({single_quotes: true}))
        .pipe(gulp.dest(targetPath + '/dist/dist_js/app'));
    }
    
    function webPluginCopyFiles() {
      const targetPath = './dist/web/www';
      return merge(
        // Copy Js (and remove unused code)
        gulp.src('./www/plugins/**/*.js')
          .pipe(removeCode({"no-device": true}))
          .pipe(jshint())
          .pipe(gulp.dest(targetPath + '/plugins')),
    
        // Copy HTML templates (and remove unused code)
        gulp.src('./www/plugins/**/*.html')
          .pipe(removeCode({"no-device": true}))
          .pipe(removeHtml('.hidden-no-device'))
          .pipe(removeHtml('[remove-if][remove-if="no-device"]'))
          .pipe(htmlmin())
          .pipe(gulp.dest(targetPath + '/plugins')),
    
        // Transform i18n into JS
        gulp.src(paths.ng_translate_plugin)
          .pipe(jsonlint())
          .pipe(jsonlint.reporter())
          .pipe(sort())
          .pipe(ngTranslate({standalone:true, module: 'cesium.plugins.translations'}))
          .pipe(gulp.dest(targetPath + '/dist/dist_js/plugins')),
    
        // Copy plugin CSS
        gulp.src(paths.css_plugin)
          .pipe(gulp.dest(targetPath + '/dist/dist_css/plugins')),
    
        // Copy Leaflet images
        pluginLeafletImages(targetPath + '/img'),
    
        // Copy Leaflet CSS
        gulp.src('./www/css/**/leaflet.*')
          .pipe(gulp.dest(targetPath + '/css'))
    
      );
    }
    
    function webPluginNgTemplate() {
      var targetPath = './dist/web/www';
      return gulp.src(targetPath + '/plugins/**/*.html')
        .pipe(sort())
        .pipe(templateCache({
          standalone: true,
          module: "cesium.plugins.templates",
          root: "plugins/"
        }))
        .pipe(gulp.dest(targetPath + '/dist/dist_js/plugins'));
    }
    
    function webPluginNgAnnotate() {
      var targetPath = './dist/web/www';
      return gulp.src(targetPath + '/plugins/**/*.js')
        .pipe(ngAnnotate({single_quotes: true}))
        .pipe(gulp.dest(targetPath + '/dist/dist_js/plugins'));
    }
    
    function webUglify() {
      const targetPath = './dist/web/www';
      const enableUglify = argv.release || argv.uglify || false;
      const version = JSON.parse(fs.readFileSync('./package.json', 'utf8')).version;
    
      if (enableUglify) {
    
        log(colors.green('Minify JS and CSS files...'));
    
        const indexFilter = filter('**/index.html', {restore: true});
        const jsFilter = filter(["**/*.js", '!**/config.js'], {restore: true});
        const cssFilter = filter("**/*.css", {restore: true});
    
        // Process index.html
        return gulp.src(targetPath + '/index.html')
          .pipe(useref({}, lazypipe().pipe(sourcemaps.init, { loadMaps: true })))  // Concatenate with gulp-useref
    
          // Process JS
          .pipe(jsFilter)
          .pipe(uglify(uglifyBaseOptions)) // Minify javascript files
          .pipe(jsFilter.restore)
    
          // Process CSS
          .pipe(cssFilter)
          .pipe(csso())               // Minify any CSS sources
          .pipe(cssFilter.restore)
    
          // Add version to file path
          .pipe(indexFilter)
          .pipe(replace(/"(dist_js\/[a-zA-Z0-9]+).js"/g, '"$1.js?v=' + version + '"'))
          .pipe(replace(/"(dist_css\/[a-zA-Z0-9]+).css"/g, '"$1.css?v=' + version + '"'))
          .pipe(indexFilter.restore)
    
          .pipe(sourcemaps.write('maps'))
    
          .pipe(gulp.dest(targetPath));
      }
      else {
        return Promise.resolve();
      }
    }
    
    function webIntegrity() {
      const targetPath = './dist/web/www';
    
      const enableIntegrity = argv.release || false;
    
      if (enableIntegrity) {
        log(colors.green('Create index.integrity.html... '));
    
        // Process index.html
        return gulp.src(targetPath + '/index.html', {base: targetPath})
    
          // Add an integrity hash
          .pipe(sriHash())
    
          .pipe(rename({ extname: '.integrity.html' }))
          .pipe(gulp.dest(targetPath));
      }
      else {
        return Promise.resolve();
      }
    }
    
    function webApiUglify() {
      const targetPath = './dist/web/www';
      const version = JSON.parse(fs.readFileSync('./package.json', 'utf8')).version;
    
      const jsFilter = filter(["**/*.js", '!**/config.js'], {restore: true});
      const cssFilter = filter("**/*.css", {restore: true});
      const indexFilter = filter('**/index.html', {restore: true});
    
      // Skip if not required
      const enableUglify = argv.release || argv.uglify || false;
      if (enableUglify) {
        log(colors.green('API: Minify JS and CSS files...'));
    
        // Process api/index.html
        return gulp.src(targetPath + '/*/index.html')
    
          .pipe(useref({}, lazypipe().pipe(sourcemaps.init, { loadMaps: true })))  // Concatenate with gulp-useref
    
          // Process JS
          .pipe(jsFilter)
          .pipe(uglify(uglifyBaseOptions)) // Minify any javascript sources
          .pipe(jsFilter.restore)
    
          // Process CSS
          .pipe(cssFilter)
          .pipe(csso())               // Minify any CSS sources
          .pipe(cssFilter.restore)
    
          .pipe(indexFilter)
    
          // Add version to files path
          .pipe(replace(/"(dist_js\/[a-zA-Z0-9-.]+).js"/g, '"$1.js?v=' + version + '"'))
          .pipe(replace(/"(dist_css\/[a-zA-Z0-9-.]+).css"/g, '"$1.css?v=' + version + '"'))
    
          .pipe(replace("dist_js", "../dist_js"))
          .pipe(replace("dist_css", "../dist_css"))
          .pipe(replace("config.js", "../config.js"))
          .pipe(indexFilter.restore)
    
          .pipe(sourcemaps.write('maps'))
    
          .pipe(gulp.dest(targetPath));
      }
    
      else {
        log(colors.red('API: Skipping minify JS and CSS files') + colors.grey(' (missing options --release or --uglify)'));
    
        return gulp.src(targetPath + '/*/index.html')
          .pipe(useref())             // Concatenate with gulp-useref
    
          .pipe(indexFilter)
          .pipe(replace("dist_js", "../dist_js"))
          .pipe(replace("dist_css", "../dist_css"))
          .pipe(replace("config.js", "../config.js"))
          .pipe(indexFilter.restore)
    
          .pipe(gulp.dest(targetPath));
      }
    }
    
    function webCleanUnusedFiles(done) {
      log(colors.green('Clean unused files...'));
      const targetPath = './dist/web/www';
      const enableUglify = argv.release || argv.uglify || false;
      const cleanSources = enableUglify;
      const debugOptions = {...debugBaseOptions,
        title: 'Deleting',
        showCount: !argv.debug
      };
    
      if (cleanSources) {
        return merge(
          // Clean core JS
          gulp.src(targetPath + '/js/**/*.js', {read: false})
            .pipe(debug(debugOptions))
            .pipe(clean()),
    
          // Clean core CSS
          gulp.src(targetPath + '/css/**/*.css', {read: false})
            .pipe(debug(debugOptions))
            .pipe(clean()),
    
          // Clean plugins JS + CSS
          gulp.src(targetPath + '/plugins/**/*.js', {read: false})
            .pipe(debug(debugOptions))
            .pipe(clean()),
          gulp.src(targetPath + '/plugins/**/*.css', {read: false})
            .pipe(debug(debugOptions))
            .pipe(clean()),
    
          // Unused maps/config.js.map
          gulp.src(targetPath + '/maps/config.js.map', {read: false, allowEmpty: true})
            .pipe(debug(debugOptions))
            .pipe(clean())
        )
          .on('end', done);
      }
    
      if (done) done();
    }
    
    
    function webCleanUnusedDirectories() {
      log(colors.green('Clean unused directories...'));
      const enableUglify = argv.release || argv.uglify || false;
      const debugOptions = { ...debugBaseOptions,
        title: 'Deleting',
        showCount: !argv.debug
      };
    
      // Clean dir
      const wwwPath = './dist/web/www';
    
      let patterns = [
        wwwPath + '/templates',
        wwwPath + '/plugins'
      ];
    
      if (enableUglify) {
        patterns = patterns.concat([
          wwwPath + '/js',
          wwwPath + '/css',
          wwwPath + '/dist',
          wwwPath + '/lib/*',
          //  Keep IonIcons font
          '!' + wwwPath + '/lib/ionic',
          wwwPath + '/lib/ionic/*',
          '!' + wwwPath + '/lib/ionic/fonts',
    
          //  Keep RobotoDraft font
          '!' + wwwPath + '/lib/robotodraft',
          wwwPath + '/lib/robotodraft/*',
          '!' + wwwPath + '/lib/robotodraft/fonts'
        ]);
      }
      else {
        patterns = patterns.concat([
          wwwPath + '/js/*',
          '!' + wwwPath + '/js/vendor',
          wwwPath + '/dist_css',
          wwwPath + '/dist_js'
        ]);
      }
    
      return gulp.src(patterns, {read: false, allowEmpty: true})
        .pipe(debug(debugOptions))
        .pipe(clean());
    }
    
    function webZip() {
      const tmpPath = './dist/web/www';
      const version = JSON.parse(fs.readFileSync('./package.json', 'utf8')).version;
      const txtFilter = filter(["**/*.txt"], { restore: true });
    
      return gulp.src(tmpPath + '/**/*.*')
    
        // Process TXT files: Add the UTF-8 BOM character
        .pipe(txtFilter)
        .pipe(header('\ufeff'))
        .pipe(txtFilter.restore)
    
        .pipe(zip('cesium-v'+version+'-web.zip'))
    
        .pipe(gulp.dest('./dist/web/build'));
    }
    
    function webExtClean() {
      return del([
        './dist/web/ext'
      ]);
    }
    
    function webExtCopyFiles() {
      const wwwPath = './dist/web/www';
      const resourcesPath = './resources/web-ext';
      log(colors.green('Copy web extension files...'));
    
      const version = JSON.parse(fs.readFileSync('./package.json', 'utf8')).version;
      const manifestFilter = filter(["**/manifest.json"], { restore: true });
      const txtFilter = filter(["**/*.txt"], { restore: true });
    
      // Copy files
      return gulp.src([
        wwwPath + '/**/*',
    
        // Skip API files
        '!' + wwwPath + '/api',
        '!' + wwwPath + '/dist_js/*-api.js',
        '!' + wwwPath + '/dist_css/*-api.css',
        '!' + wwwPath + '/maps/dist_js/*-api.js.map',
        '!' + wwwPath + '/maps/dist_css/*-api.css.map',
    
        // Skip web manifest
        '!' + wwwPath + '/manifest.json',
    
        // Add specific resource (and overwrite the default 'manifest.json')
        resourcesPath + '/**/*.*'
      ])
    
      // Process TXT files: Add the UTF-8 BOM character
      .pipe(txtFilter)
      .pipe(header('\ufeff'))
      .pipe(txtFilter.restore)
    
      // Replace version in 'manifest.json' file
      .pipe(manifestFilter)
      .pipe(replace(/\"version\": \"[^\"]*\"/, '"version": "' + version + '"'))
      .pipe(manifestFilter.restore)
    
      .pipe(gulp.dest('./dist/web/ext'));
    }
    
    function webExtensionZip() {
      const srcPath = './dist/web/ext';
      const distPath = './dist/web/build';
      const version = JSON.parse(fs.readFileSync('./package.json', 'utf8')).version;
    
      return gulp.src(srcPath + '/**/*.*')
        .pipe(zip('cesium-v'+version+'-extension.zip'))
        .pipe(gulp.dest(distPath));
    }
    
    function webBuildSuccess(done) {
      var version = JSON.parse(fs.readFileSync('./package.json', 'utf8')).version;
      log(colors.green("Web artifact created at: 'dist/web/build/cesium-v" + version + "-web.zip'"));
      if (done) done();
    }
    
    function webExtBuildSuccess(done) {
      var version = JSON.parse(fs.readFileSync('./package.json', 'utf8')).version;
      log(colors.green("Web extension artifact created at: 'dist/web/build/cesium-v" + version + "-extension.zip'"));
      if (done) done();
    }
    
    function cdvAddPlatformToBodyTag() {
      log(colors.green('Add platform CSS class to <body>... '));
    
      const projectRoot = argv.root || '.';
      const platform = argv.platform || 'android';
      let wwwPath;
      if (platform === 'android') {
        wwwPath = path.join(projectRoot, 'platforms', platform, 'app','src','main','assets','www');
      } else {
        wwwPath = path.join(projectRoot, 'platforms', platform, 'www');
      }
      const indexPath = path.join(wwwPath, 'index.html');
    
      // no opening body tag, something's wrong
      if (!fs.existsSync(indexPath)) throw new Error('Unable to find the file ' + indexPath +'!');
    
      // add the platform class to the body tag
      try {
        const platformClass = 'platform-' + platform;
        const cordovaClass = 'platform-cordova platform-webview';
    
        let html = fs.readFileSync(indexPath, 'utf8');
    
        // get the body tag
        let matches = html && html.match(/<body[^>/]+>/gi)
        const bodyTag = matches && matches[0];
        // no opening body tag, something's wrong
        if (!bodyTag) throw new Error('No <body> element found in file ' + indexPath);
    
        if (bodyTag.indexOf(platformClass) > -1) return; // already added
    
        let newBodyTag = '' + bodyTag;
        matches = bodyTag.match(/ class=["|'](.*?)["|']/gi);
        const classAttr = matches && matches[0];
        if (classAttr) {
          // body tag has existing class attribute, add the classname
          let endingQuote = classAttr.substring(classAttr.length - 1);
          let newClassAttr = classAttr.substring(0, classAttr.length - 1);
          newClassAttr += ' ' + platformClass + ' ' + cordovaClass + endingQuote;
          newBodyTag = newBodyTag.replace(classAttr, newClassAttr);
    
        } else {
          // add class attribute to the body tag
          newBodyTag = newBodyTag.replace('>', ' class="' + platformClass + ' ' + cordovaClass + '">');
        }
    
        html = html.replace(bodyTag, newBodyTag);
    
        fs.writeFileSync(indexPath, html, 'utf8');
    
        return Promise.resolve();
      } catch (e) {
        return Promise.reject(e);
      }
    }
    
    function cdvNgAnnotate() {
      log(colors.green('Building JS files... '));
    
      const projectRoot = argv.root || '.';
      const platform = argv.platform || 'android';
      let wwwPath;
      if (platform === 'android') {
        wwwPath = path.join(projectRoot, 'platforms', platform, 'app','src','main','assets','www');
      } else {
        wwwPath = path.join(projectRoot, 'platforms', platform, 'www');
      }
    
      const jsFilter = filter(["**/*.js", "!**/vendor/*"]);
    
      return merge(
    
        // Ng annotate app JS file
        gulp.src(wwwPath + '/js/**/*.js')
          .pipe(jsFilter)
          .pipe(ngAnnotate({single_quotes: true}))
          .pipe(gulp.dest(wwwPath + '/dist/dist_js/app')),
    
        // Ng annotate app JS file
        gulp.src(wwwPath + '/plugins/**/*.js')
          .pipe(ngAnnotate({single_quotes: true}))
          .pipe(gulp.dest(wwwPath + '/dist/dist_js/plugins'))
    
      );
    }
    
    function cdvRemoveCode() {
      log(colors.green('Removing code... '));
    
      const projectRoot = argv.root || '.';
      const platform = argv.platform || 'android';
      let wwwPath;
      if (platform === 'android') {
        wwwPath = path.join(projectRoot, 'platforms', platform, 'app','src','main','assets','www');
      } else {
        wwwPath = path.join(projectRoot, 'platforms', platform, 'www');
      }
    
      const pluginPath = path.join(wwwPath, 'plugins', 'es');
    
      // Compute options {device-<platform>: true}
      let removeCodeOptions = {};
      removeCodeOptions[platform] = true; // = {<platform>: true}
    
      const htmlminOptions = {removeComments: true, collapseWhitespace: true};
      const debugOptions = {...debugBaseOptions,
        showCount: false
      };
    
      // Do not remove desktop code for iOS and macOS (support for tablets and desktop macs)
      if (platform !== 'ios' && platform !== 'osx') {
        // Removing unused code for device...
        return merge(
          // Remove unused HTML tags
          gulp.src(path.join(wwwPath, 'templates', '**', '*.html'))
            .pipe(debug(debugOptions))
            .pipe(removeCode({device: true}))
            .pipe(removeCode(removeCodeOptions))
            .pipe(removeHtml('.hidden-xs.hidden-sm'))
            .pipe(removeHtml('.hidden-device'))
            .pipe(removeHtml('[remove-if][remove-if="device"]'))
            .pipe(htmlmin(htmlminOptions))
            .pipe(gulp.dest(wwwPath + '/templates')),
    
          gulp.src(path.join(pluginPath, '**', '*.html'))
            .pipe(debug(debugOptions))
            .pipe(removeCode({device: true}))
            .pipe(removeCode(removeCodeOptions))
            .pipe(removeHtml('.hidden-xs.hidden-sm'))
            .pipe(removeHtml('.hidden-device'))
            .pipe(removeHtml('[remove-if][remove-if="device"]'))
            .pipe(htmlmin(htmlminOptions))
            .pipe(gulp.dest(pluginPath)),
    
          gulp.src(path.join(wwwPath, 'index.html'))
            .pipe(debug(debugOptions))
            .pipe(removeCode({device: true}))
            .pipe(removeCode(removeCodeOptions))
            .pipe(removeHtml('.hidden-xs.hidden-sm'))
            .pipe(removeHtml('.hidden-device'))
            .pipe(removeHtml('[remove-if][remove-if="device"]'))
            .pipe(htmlmin(/*no options, to keep comments*/))
            .pipe(gulp.dest(wwwPath)),
    
          // Remove unused JS code + add ng annotations
          gulp.src(path.join(wwwPath, 'js', '**', '*.js'))
            .pipe(debug(debugOptions))
            .pipe(removeCode({device: true}))
            .pipe(removeCode(removeCodeOptions))
            .pipe(ngAnnotate({single_quotes: true}))
            .pipe(gulp.dest(wwwPath + '/dist/dist_js/app')),
    
          gulp.src([pluginPath + '/js/**/*.js'])
            .pipe(debug(debugOptions))
            .pipe(removeCode({device: true}))
            .pipe(removeCode(removeCodeOptions))
            .pipe(ngAnnotate({single_quotes: true}))
            .pipe(gulp.dest(wwwPath + '/dist/dist_js/plugins'))
        );
      } else {
        return merge(
          gulp.src(path.join(wwwPath, 'templates', '**', '*.html'))
            .pipe(htmlmin(htmlminOptions))
            .pipe(gulp.dest(wwwPath + '/templates')),
    
          gulp.src(path.join(pluginPath, '**', '*.html'))
            .pipe(htmlmin(htmlminOptions))
            .pipe(gulp.dest(pluginPath)),
    
          gulp.src(path.join(wwwPath, 'index.html'))
            .pipe(gulp.dest(wwwPath)),
    
          gulp.src(path.join(wwwPath, 'js', '**', '*.js'))
            .pipe(ngAnnotate({single_quotes: true}))
            .pipe(gulp.dest(wwwPath + '/dist/dist_js/app')),
    
          gulp.src([pluginPath + '/js/**/*.js'])
            .pipe(gulp.dest(wwwPath + '/dist/dist_js/plugins'))
        );
      }
    }
    
    function cdvNgTemplate() {
      log(colors.green('Building template files...'));
    
      const projectRoot = argv.root || '.';
      const platform = argv.platform || 'android';
    
      let wwwPath;
      if (platform === 'android') {
        wwwPath = path.join(projectRoot, 'platforms', platform, 'app','src','main','assets','www');
      } else {
        wwwPath = path.join(projectRoot, 'platforms', platform, 'www');
      }
      let distJsPath = path.join(wwwPath, 'dist', 'dist_js', 'app');
      let pluginDistJsPath = path.join(wwwPath, 'dist', 'dist_js', 'plugins');
      const debugOptions = {
        title: 'Processing',
        minimal: true,
        showFiles: argv.debug || false,
        showCount: false,
        logger: m => log(colors.grey(m))
      };
    
      // Concat templates into a JS
      return merge(
        gulp.src(path.join(wwwPath, 'templates', '**', '*.html'))
          .pipe(debug(debugOptions))
          .pipe(templateCache({
            standalone: true,
            module: "cesium.templates",
            root: "templates/"
          }))
          .pipe(gulp.dest(distJsPath)),
    
        gulp.src(path.join(wwwPath, 'plugins', '*', 'templates', '**', '*.html'))
          .pipe(debug(debugOptions))
          .pipe(templateCache({
            standalone: true,
            module: "cesium.plugins.templates",
            root: "plugins/"
          }))
          .pipe(gulp.dest(pluginDistJsPath))
      );
    }
    function cdvNgTranslate() {
      log(colors.green('Building translation files...'));
    
      const projectRoot = argv.root || '.';
      const platform = argv.platform || 'android';
    
      let wwwPath;
      if (platform === 'android') {
        wwwPath = path.join(projectRoot, 'platforms', platform, 'app', 'src', 'main', 'assets', 'www');
      } else {
        wwwPath = path.join(projectRoot, 'platforms', platform, 'www');
      }
      let distJsPath = path.join(wwwPath, 'dist', 'dist_js', 'app');
      let pluginDistJsPath = path.join(wwwPath, 'dist', 'dist_js', 'plugins');
    
      const debugOptions = {
        title: 'Processing',
        minimal: true,
        showFiles: argv.debug || false,
        showCount: false,
        logger: m => log(colors.grey(m))
      };
    
      // Concat templates into a JS
      return merge(
          gulp.src(wwwPath + '/i18n/locale-*.json')
            .pipe(debug(debugOptions))
            .pipe(ngTranslate({standalone: true, module: 'cesium.translations'}))
            .pipe(gulp.dest(distJsPath)),
    
          gulp.src(wwwPath + '/plugins/*/i18n/locale-*.json')
            .pipe(debug(debugOptions))
            .pipe(ngTranslate({standalone: true, module: 'cesium.plugins.translations'}))
            .pipe(gulp.dest(pluginDistJsPath))
        );
    }
    
    function cdvUglify() {
    
      const projectRoot = argv.root || '.';
      const platform = argv.platform || 'android';
    
      let wwwPath;
      if (platform === 'android') {
        wwwPath = path.join(projectRoot, 'platforms', platform, 'app', 'src', 'main', 'assets', 'www');
      } else {
        wwwPath = path.join(projectRoot, 'platforms', platform, 'www');
      }
      let indexPath = path.join(wwwPath, 'index.html');
    
      // Skip if not required
      const enableUglify = argv.release || argv.uglify || false;
      if (enableUglify) {
        log(colors.green('Minify JS and CSS files...'));
    
        // WARN: uglify only libs, to keep sources readable (need by free repo)
        const jsLibFilter = filter(['*/lib/**/*.js', '*/js/vendor/**/*.js'], {restore: true}); // External libs only
        const cssFilter = filter("**/*.css", {restore: true});
        const cdvUglifyOptions = {
          ...uglifyBaseOptions,
          ecma: '5'
        };
        const debugOptions = {
          title: 'Minifying',
          minimal: true,
          showFiles: argv.debug || false,
          showCount: false,
          logger: m => log(colors.grey(m))
        };
    
        return gulp.src(indexPath)
          .pipe(useref())             // Concatenate with gulp-useref
    
          // Process JS
          .pipe(jsLibFilter)
          .pipe(debug(debugOptions))
          .pipe(uglify(cdvUglifyOptions))// Minify javascript sources
          .pipe(jsLibFilter.restore)
    
          // Process CSS
          .pipe(cssFilter)
          .pipe(debug(debugOptions))
          .pipe(csso())               // Minify any CSS sources
          .pipe(cssFilter.restore)
    
          .pipe(gulp.dest(wwwPath));
      }
      else {
        log(colors.red('Skipping minify JS and CSS files') + colors.grey(' (missing options --release or --uglify)'));
        return Promise.resolve();
      }
    }
    
    function cdvCleanUnusedDirectories() {
      log(colors.green('Clean unused directories...'));
    
      const projectRoot = argv.root || '.';
      const platform = argv.platform || 'android';
    
      let wwwPath;
      if (platform === 'android') {
        wwwPath = path.join(projectRoot, 'platforms', platform, 'app', 'src', 'main', 'assets', 'www');
      } else {
        wwwPath = path.join(projectRoot, 'platforms', platform, 'www');
      }
    
      const enableUglify = argv.release || argv.uglify || false;
      const debugOptions = {
        title: 'Deleting',
        minimal: true,
        showFiles: argv.debug || false,
        showCount: !argv.debug,
        logger: m => log(colors.grey(m))
      };
    
      let patterns = [
        wwwPath + '/api',
    
        // Remove HTML templates - replaced by ngTemplate()
        wwwPath + '/templates',
    
        // Remove Cesium plugins
        // (WARN: remove one by one, to keep Cordova plugins)
        wwwPath + '/plugins/es',
        wwwPath + '/plugins/graph',
        wwwPath + '/plugins/map',
        wwwPath + '/plugins/rml9',
    
        // Remove translations - replaced by ngTranslate()
        wwwPath + '/**/i18n',
      ];
    
      if (enableUglify) {
        patterns = patterns.concat([
          wwwPath + '/js',
          wwwPath + '/css', // Have been replaced by useref(), into 'dist_css'
          wwwPath + '/dist', // Have been replaced by useref(), into 'dist_js'
          wwwPath + '/cordova-js-src',
    
          // Clean lib directory...
          wwwPath + '/lib/*',
    
          // ...but Keep IonIcons font
          '!' + wwwPath + '/lib/ionic',
          wwwPath + '/lib/ionic/*',
          '!' + wwwPath + '/lib/ionic/fonts',
    
          // ...but Keep RobotoDraft font
          '!' + wwwPath + '/lib/robotodraft',
          wwwPath + '/lib/robotodraft/*',
          '!' + wwwPath + '/lib/robotodraft/fonts'
        ]);
      }
      else {
        patterns = patterns.concat([
          wwwPath + '/js/*', // Have been replace into dist/dist_js
          '!' + wwwPath + '/js/vendor', // BUT keep vendor lib
        ]);
      }
    
      return gulp.src(patterns, {read: false, allowEmpty: true})
        .pipe(debug(debugOptions))
        .pipe(clean());
    }
    
    
    function cdvCopyBuildFiles() {
      log(colors.green('Copy build files... '));
    
      const projectRoot = argv.root || '.';
      const platform = argv.platform || 'android';
    
      const srcPath = path.join(projectRoot, 'resources', platform, 'build');
      const targetPath = path.join(projectRoot, 'platforms', platform);
      const debugOptions = { ...debugBaseOptions, title: 'Copying',
        showFiles: argv.debug || false,
        showCount: !argv.debug
      };
    
      if (fs.existsSync(srcPath)) {
        return gulp.src(srcPath + '/**/*.*')
          .pipe(debug(debugOptions))
          .pipe(gulp.dest(targetPath));
      }
      else {
        log(colors.blue(' Directory ' + srcPath + 'not found. Skipping copy to ' + targetPath));
        return Promise.resolve();
      }
    }
    
    function cdvAndroidManifest() {
    
      const projectRoot = argv.root || '.';
      const platform = argv.platform || 'android';
      if (platform !== 'android') return Promise.resolve(); // Skip
    
      const srcMainPath = path.join(projectRoot, 'platforms', platform, 'app', 'src', 'main');
      const androidManifestFile = path.join(srcMainPath, 'AndroidManifest.xml');
    
      log(colors.green('Patch Android manifest... ') + colors.grey(androidManifestFile));
    
      if (!fs.existsSync(androidManifestFile)) {
        throw Error("Missing required file " + androidManifestFile);
      }
    
      return gulp.src(androidManifestFile)
    
        // Add 'tools' namespace to root tag
        .pipe(replace(/(xmlns:android="http:\/\/schemas.android.com\/apk\/res\/android")\s*>/g, '$1 xmlns:tools="http://schemas.android.com/tools">'))
    
        // Use AndroidX
        .pipe(replace(/\s+tools:replace="android:appComponentFactory"/, ''))
        .pipe(replace(/\s+android:appComponentFactory="[^"]+"/, ''))
        .pipe(replace(/(\s*<application)\s*/, '$1 tools:replace="android:appComponentFactory" android:appComponentFactory="androidx.core.app.CoreComponentFactory" '))
    
        // remove all <uses-sdk>
        .pipe(replace(/<uses-sdk [^>]+\/>/g, ''))
    
        // add <uses-sdk> (tools:overrideLibrary)
        .pipe(replace(/(<\/manifest>)/, '    <uses-sdk tools:overrideLibrary="org.kaliumjni.lib,org.apache.cordova" />\n$1'))
    
        // Add URI scheme web+june
        // Patch invalid intent-filter (should be a bug of cordova-plugin-customurlschema)
        // FIXME : this cause too many intent-filter are appended, on each build
        //.pipe(replace('<data android:host=" " android:pathPrefix="/" android:scheme=" " />', '<data android:scheme="web+june" />'))
    
        .pipe(gulp.dest(srcMainPath));
    }
    
    function cdvAndroidCheckSigning() {
    
      const projectRoot = argv.root || '.';
      const platform = argv.platform || 'android';
      if (platform !== 'android') return Promise.resolve(); // Skip
    
      const targetPath = path.join(projectRoot, 'platforms', platform);
      const signingFile = path.join(targetPath, 'release-signing.properties');
    
      // Check signing file exists
      if (fs.existsSync(targetPath) && !fs.existsSync(signingFile)) {
        log(colors.blue('WARNING: Missing file ' + signingFile));
        log(colors.blue('  Please create it manually, otherwise release APK files will NOT be signed! '));
      }
    
      return Promise.resolve();
    }
    
    function cdvAsHook(wrapper) {
    
      return (done, projectRoot, platform) => {
        projectRoot = (typeof projectRoot === 'string' && projectRoot) || argv.root || '.';
        platform = ((typeof platform === 'string' && platform) || argv.platform || 'android').toLowerCase();
    
        // Override arguments, to pass it to other tasks
        argv.root = projectRoot;
        argv.platform = platform;
    
        wrapper(done);
      }
    }
    
    function help() {
      log(colors.green("Usage: gulp {config|webBuild|webExtBuild} OPTIONS"));
      log(colors.green(""));
      log(colors.green("NAME"));
      log(colors.green(""));
      log(colors.green("  config --env <config_name>  Configure environment (create file `www/config.js`). "));
      log(colors.green("  build                       Build from sources (CSS and JS)"));
      log(colors.green("  webBuild                    Build ZIP artifact"));
      log(colors.green("  webExtBuild                 Build web extension artifact (browser module)"));
      log(colors.green(""));
      log(colors.green("OPTIONS"));
      log(colors.green(""));
      log(colors.green("  --release                   Release build (with uglify and sourcemaps)"));
      log(colors.green("  --uglify                    Build using uglify plugin"));
    }
    
    /* --------------------------------------------------------------------------
       -- Combine task
       --------------------------------------------------------------------------*/
    const translate = gulp.series(appNgTranslate, pluginNgTranslate);
    const template = gulp.series(appNgTemplate, pluginNgTemplate);
    const appAndPluginSass = gulp.series(appSass, pluginSass);
    const app = gulp.series(appSass, appNgTemplate, appNgAnnotate, appNgTranslate);
    const plugin = gulp.series(pluginSass, pluginNgTemplate, pluginNgAnnotate, pluginNgTranslate);
    const build = gulp.series(appLicense, app, plugin);
    
    const webApp = gulp.series(appSass, webCopyFiles, webNgTemplate, webAppNgAnnotate);
    const webPlugin = gulp.series(pluginSass, webPluginCopyFiles, webPluginNgTemplate, webPluginNgAnnotate);
    const webCompile = gulp.series(
      webClean,
      webApp,
      webPlugin,
      webUglify,
      webIntegrity,
      webApiUglify,
      webCleanUnusedFiles,
      webCleanUnusedDirectories
    );
    
    // note : Do not call config, to keep same config between web and webExt artifacts
    const webBuild = gulp.series(
      webClean,
      webCompile,
      webZip,
      webBuildSuccess
    );
    const webExtCompile = gulp.series(
      webExtClean,
      webCompile,
      webExtCopyFiles
    );
    
    // note : Do not call config, to keep same config between web and webExt artifacts
    const webExtBuild = gulp.series(
      webExtCompile,
      webExtensionZip,
      webExtBuildSuccess
    );
    
    
    
    /* --------------------------------------------------------------------------
       -- Define public tasks
       --------------------------------------------------------------------------*/
    
    exports.help = help;
    exports.config = appConfig;
    exports.license = appLicense;
    exports.sass = appAndPluginSass;
    exports.translate = translate;
    exports.template = template;
    exports.clean = appAndPluginClean;
    exports.lint = appAndPluginLint;
    exports.annotate = gulp.series(appNgAnnotate, pluginNgAnnotate);
    exports.watch = appAndPluginWatch;
    exports.build = build;
    
    // Web
    exports.webCompile = webCompile;
    exports.webBuild = webBuild;
    exports['build:web'] = exports.webBuild; // Alias
    
    // Web extension
    exports.webExtClean = webExtClean;
    exports.webExtCompile = webExtCompile;
    exports.webExtBuild = webExtBuild;
    exports.webExtCopyFiles = webExtCopyFiles;
    exports['build:webExt'] = exports.webExtBuild; // Alias
    
    // Cordova (hooks)
    const cdvAfterPrepare = gulp.series(
      gulp.parallel(cdvNgAnnotate, cdvAddPlatformToBodyTag),
      cdvRemoveCode,
      gulp.parallel(cdvNgTemplate, cdvNgTranslate),
      cdvUglify,
      gulp.parallel(cdvCleanUnusedDirectories, cdvCopyBuildFiles),
      // Android tasks
      gulp.parallel(cdvAndroidManifest, cdvAndroidCheckSigning),
    );
    exports.cdvAfterPrepare = cdvAsHook(cdvAfterPrepare);
    
    const cdvBeforeCompile = gulp.series(
      cdvCleanUnusedDirectories,
      cdvCopyBuildFiles,
      cdvAndroidManifest,
      cdvAndroidCheckSigning
    );
    exports.cdvBeforeCompile = cdvAsHook(cdvBeforeCompile);
    
    exports.default = gulp.series(appConfig, build);
    exports.serveBefore = gulp.series(build, appAndPluginWatch);
    exports['ionic:serve:before'] = exports.serveBefore; // Alias need need by @ionic/cli