<commit_before>'use strict';

// # Globbing

// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'

// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'

module.exports = function (grunt) {

  // Load grunt tasks automatically
  require('load-grunt-tasks')(grunt);

  // Time how long tasks take. Can help when optimizing build times
  require('time-grunt')(grunt);

  // Configurable paths for the application
  var appConfig = {
    app: require('./bower.json').appPath || 'app',
    dist: 'dist'
  };

  // Define the configuration for all the tasks
  grunt.initConfig({

    // Project settings
    yeoman: appConfig,

    // Watches files for changes and runs tasks based on the changed files
    watch: {
      bower: {
        files: ['bower.json'],
        tasks: ['wiredep']
      },
      js: {
        files: ['<%= yeoman.app %>/{,*/}*.js'],
        tasks: ['newer:jshint:all'],
        options: {
          livereload: '<%= connect.options.livereload %>'
        }
      },
      jsTest: {
        files: ['test/spec/{,*/}*.js'],
        tasks: ['newer:jshint:test', 'karma']
      },
      styles: {
        files: ['<%= yeoman.app %>/styles/{,*/}*.css'],
        tasks: ['newer:copy:styles', 'autoprefixer']
      },
      gruntfile: {
        files: ['Gruntfile.js']
      },
      livereload: {
        options: {
          livereload: '<%= connect.options.livereload %>'
        },
        files: [
          '<%= yeoman.app %>/{,*/}*.html',
          '.tmp/styles/{,*/}*.css',
          '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
        ]
      }
    },

    // The actual grunt server settings
    connect: {
      options: {
        port: 4000,
        // Change this to '0.0.0.0' to access the server from outside.
        hostname: 'localhost',
        livereload: 4002
      },
      livereload: {
        options: {
          open: true,
          middleware: function (connect) {
            return [
              connect.static('.tmp'),
              connect().use(
                '/bower_components',
                connect.static('./bower_components')
              ),
              connect().use(
                '/app/styles',
                connect.static('./app/styles')
              ),
              connect.static(appConfig.app)
            ];
          }
        }
      },
      test: {
        options: {
          port: 9001,
          middleware: function (connect) {
            return [
              connect.static('.tmp'),
              connect.static('test'),
              connect().use(
                '/bower_components',
                connect.static('./bower_components')
              ),
              connect.static(appConfig.app)
            ];
          }
        }
      },
      dist: {
        options: {
          open: true,
          base: '<%= yeoman.dist %>'
        }
      }
    },

    // Make sure code styles are up to par and there are no obvious mistakes
    jshint: {
      options: {
        jshintrc: '.jshintrc',
        reporter: require('jshint-stylish')
      },
      all: {
        src: [
          'Gruntfile.js',
          '<%= yeoman.app %>/scripts/{,*/}*.js'
        ]
      },
      test: {
        options: {
          jshintrc: 'test/.jshintrc'
        },
        src: ['test/spec/{,*/}*.js']
      }
    },

    // Empties folders to start fresh
    clean: {
      dist: {
        files: [{
          dot: true,
          src: [
            '.tmp',
            '<%= yeoman.dist %>/{,*/}*',
            '!<%= yeoman.dist %>/.git{,*/}*'
          ]
        }]
      },
      server: '.tmp'
    },

    // Add vendor prefixed styles
    autoprefixer: {
      options: {
        browsers: ['last 1 version']
      },
      server: {
        options: {
          map: true,
        },
        files: [{
          expand: true,
          cwd: '.tmp/styles/',
          src: '{,*/}*.css',
          dest: '.tmp/styles/'
        }]
      },
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/styles/',
          src: '{,*/}*.css',
          dest: '.tmp/styles/'
        }]
      }
    },

    // Automatically inject Bower components into the app
    wiredep: {
      app: {
        src: ['<%= yeoman.app %>/index.html'],
        ignorePath:  /\.\.\//
      },
      test: {
        devDependencies: true,
        src: '<%= karma.unit.configFile %>',
        ignorePath:  /\.\.\//,
        fileTypes:{
          js: {
            block: /(([\s\t]*)\/{2}\s*?bower:\s*?(\S*))(\n|\r|.)*?(\/{2}\s*endbower)/gi,
              detect: {
                js: /'(.*\.js)'/gi
              },
              replace: {
                js: '\'{{filePath}}\','
              }
            }
          }
      }
    },

    // Renames files for browser caching purposes
    filerev: {
      dist: {
        src: [
          '<%= yeoman.dist %>/scripts/{,*/}*.js',
          '<%= yeoman.dist %>/styles/{,*/}*.css',
          '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
          '<%= yeoman.dist %>/styles/fonts/*'
        ]
      }
    },

    // Reads HTML for usemin blocks to enable smart builds that automatically
    // concat, minify and revision files. Creates configurations in memory so
    // additional tasks can operate on them
    useminPrepare: {
      html: '<%= yeoman.app %>/index.html',
      options: {
        dest: '<%= yeoman.dist %>',
        flow: {
          html: {
            steps: {
              js: ['concat', 'uglifyjs'],
              css: ['cssmin']
            },
            post: {}
          }
        }
      }
    },

    // Performs rewrites based on filerev and the useminPrepare configuration
    usemin: {
      html: ['<%= yeoman.dist %>/{,*/}*.html'],
      css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
      options: {
        assetsDirs: [
          '<%= yeoman.dist %>',
          '<%= yeoman.dist %>/images',
          '<%= yeoman.dist %>/styles'
        ]
      }
    },

    // The following *-min tasks will produce minified files in the dist folder
    // By default, your `index.html`'s <!-- Usemin block --> will take care of
    // minification. These next options are pre-configured if you do not wish
    // to use the Usemin blocks.
    cssmin: {
       dist: {
         files: {
           '<%= yeoman.dist %>/styles/main.css': [
             '.tmp/styles/{,*/}*.css'
           ]
         }
       }
     },
     uglify: {
       dist: {
         files: {
           '<%= yeoman.dist %>/scripts/scripts.js': [
             '<%= yeoman.dist %>/scripts/scripts.js'
           ]
         }
       }
     },
     concat: {
       dist: {}
     },

    imagemin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.app %>/images',
          src: '**/*.{png,jpg,jpeg,gif}',
          dest: '<%= yeoman.dist %>/images'
        }]
      }
    },

    svgmin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.app %>/images',
          src: '{,*/}*.svg',
          dest: '<%= yeoman.dist %>/images'
        }]
      }
    },

    htmlmin: {
      dist: {
        options: {
          collapseWhitespace: true,
          conservativeCollapse: true,
          collapseBooleanAttributes: true,
          removeCommentsFromCDATA: true,
          removeOptionalTags: true
        },
        files: [{
          expand: true,
          cwd: '<%= yeoman.dist %>',
          src: ['*.html', '{,*/}*.html'],
          dest: '<%= yeoman.dist %>'
        }]
      }
    },

    // ng-annotate tries to make the code safe for minification automatically
    // by using the Angular long form for dependency injection.
    ngAnnotate: {
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/concat/scripts',
          src: '*.js',
          dest: '.tmp/concat/scripts'
        }]
      }
    },

    // Replace Google CDN references
    cdnify: {
      dist: {
        html: ['<%= yeoman.dist %>/*.html']
      }
    },

    // Copies remaining files to places other tasks can use
    copy: {
      dist: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%= yeoman.app %>',
          dest: '<%= yeoman.dist %>',
          src: [
            '*.{ico,png,txt}',
            '{,*/}*.html',
            'images/{,*/}*.{webp}',
            'styles/fonts/{,*/}*.*'
          ]
        }, {
          expand: true,
          cwd: '.tmp/images',
          dest: '<%= yeoman.dist %>/images',
          src: ['generated/*']
        }, {
          expand: true,
          cwd: 'bower_components/bootstrap/dist',
          src: 'fonts/*',
          dest: '<%= yeoman.dist %>'
        },{
          expand: true,
          cwd: 'bower_components/fontawesome',
          src: 'fonts/*',
          dest: '<%= yeoman.dist %>'
        }]
      },
      styles: {
        expand: true,
        cwd: '<%= yeoman.app %>/styles',
        dest: '.tmp/styles/',
        src: '{,*/}*.css'
      }
    },

    // Run some tasks in parallel to speed up the build process
    concurrent: {
      server: [
        'copy:styles'
      ],
      test: [
        'copy:styles'
      ],
      dist: [
        'copy:styles',
        'imagemin',
        'svgmin'
      ]
    },

    // Test settings
    karma: {
      unit: {
        configFile: 'test/karma.conf.js',
        singleRun: true
      }
    },

    // Execute Firebase Deploy

    exec: {
      deploy: {
        cmd: 'firebase deploy'
      },
      git_add: {
        cmd: 'git add .'
      },
      git_commit: {
        cmd: 'git commit -m "Latest Build and Firebase Deployment"'
      },
      git_push: {
        cmd: 'git push'
      }
    }

  });

  grunt.registerTask('serve', 'Compile then start a connect web server', function (target) {
    if (target === 'dist') {
      return grunt.task.run(['build', 'connect:dist:keepalive']);
    }

    grunt.task.run([
      'clean:server',
      'wiredep',
      'concurrent:server',
      'autoprefixer:server',
      'connect:livereload',
      'watch'
    ]);
  });

  grunt.registerTask('server', 'DEPRECATED TASK. Use the "serve" task instead', function (target) {
    grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
    grunt.task.run(['serve:' + target]);
  });

  grunt.registerTask('test', [
    'clean:server',
    'wiredep',
    'concurrent:test',
    'autoprefixer',
    'connect:test',
    'karma'
  ]);

  grunt.registerTask('build', [
    'clean:dist',
    'wiredep',
    'useminPrepare',
    'concurrent:dist',
    'autoprefixer',
    'concat',
    'ngAnnotate',
    'copy:dist',
    'cdnify',
    'cssmin',
    'uglify',
    'filerev',
    'usemin',
    'htmlmin'
  ]);

  grunt.registerTask('deploy', [
    'build',
    'exec:deploy',
    'exec:git_add',
    'exec:git_commit',
    "exec:git_push"
  ]);

  grunt.registerTask('default', [
    'newer:jshint',
    'test',
    'build'
  ]);
};
<commit_msg>Updated Gruntfile to resolve issue with imagemin error on build<commit_after>'use strict';

// # Globbing

// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'

// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'

module.exports = function (grunt) {

  // Load grunt tasks automatically
  require('load-grunt-tasks')(grunt);

  // Time how long tasks take. Can help when optimizing build times
  require('time-grunt')(grunt);

  // Configurable paths for the application
  var appConfig = {
    app: require('./bower.json').appPath || 'app',
    dist: 'dist'
  };

  // Define the configuration for all the tasks
  grunt.initConfig({

    // Project settings
    yeoman: appConfig,

    // Watches files for changes and runs tasks based on the changed files
    watch: {
      bower: {
        files: ['bower.json'],
        tasks: ['wiredep']
      },
      js: {
        files: ['<%= yeoman.app %>/{,*/}*.js'],
        tasks: ['newer:jshint:all'],
        options: {
          livereload: '<%= connect.options.livereload %>'
        }
      },
      jsTest: {
        files: ['test/spec/{,*/}*.js'],
        tasks: ['newer:jshint:test', 'karma']
      },
      styles: {
        files: ['<%= yeoman.app %>/styles/{,*/}*.css'],
        tasks: ['newer:copy:styles', 'autoprefixer']
      },
      gruntfile: {
        files: ['Gruntfile.js']
      },
      livereload: {
        options: {
          livereload: '<%= connect.options.livereload %>'
        },
        files: [
          '<%= yeoman.app %>/{,*/}*.html',
          '.tmp/styles/{,*/}*.css',
          '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
        ]
      }
    },

    // The actual grunt server settings
    connect: {
      options: {
        port: 4000,
        // Change this to '0.0.0.0' to access the server from outside.
        hostname: 'localhost',
        livereload: 4002
      },
      livereload: {
        options: {
          open: true,
          middleware: function (connect) {
            return [
              connect.static('.tmp'),
              connect().use(
                '/bower_components',
                connect.static('./bower_components')
              ),
              connect().use(
                '/app/styles',
                connect.static('./app/styles')
              ),
              connect.static(appConfig.app)
            ];
          }
        }
      },
      test: {
        options: {
          port: 9001,
          middleware: function (connect) {
            return [
              connect.static('.tmp'),
              connect.static('test'),
              connect().use(
                '/bower_components',
                connect.static('./bower_components')
              ),
              connect.static(appConfig.app)
            ];
          }
        }
      },
      dist: {
        options: {
          open: true,
          base: '<%= yeoman.dist %>'
        }
      }
    },

    // Make sure code styles are up to par and there are no obvious mistakes
    jshint: {
      options: {
        jshintrc: '.jshintrc',
        reporter: require('jshint-stylish')
      },
      all: {
        src: [
          'Gruntfile.js',
          '<%= yeoman.app %>/scripts/{,*/}*.js'
        ]
      },
      test: {
        options: {
          jshintrc: 'test/.jshintrc'
        },
        src: ['test/spec/{,*/}*.js']
      }
    },

    // Empties folders to start fresh
    clean: {
      dist: {
        files: [{
          dot: true,
          src: [
            '.tmp',
            '<%= yeoman.dist %>/{,*/}*',
            '!<%= yeoman.dist %>/.git{,*/}*'
          ]
        }]
      },
      server: '.tmp'
    },

    // Add vendor prefixed styles
    autoprefixer: {
      options: {
        browsers: ['last 1 version']
      },
      server: {
        options: {
          map: true,
        },
        files: [{
          expand: true,
          cwd: '.tmp/styles/',
          src: '{,*/}*.css',
          dest: '.tmp/styles/'
        }]
      },
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/styles/',
          src: '{,*/}*.css',
          dest: '.tmp/styles/'
        }]
      }
    },

    // Automatically inject Bower components into the app
    wiredep: {
      app: {
        src: ['<%= yeoman.app %>/index.html'],
        ignorePath:  /\.\.\//
      },
      test: {
        devDependencies: true,
        src: '<%= karma.unit.configFile %>',
        ignorePath:  /\.\.\//,
        fileTypes:{
          js: {
            block: /(([\s\t]*)\/{2}\s*?bower:\s*?(\S*))(\n|\r|.)*?(\/{2}\s*endbower)/gi,
              detect: {
                js: /'(.*\.js)'/gi
              },
              replace: {
                js: '\'{{filePath}}\','
              }
            }
          }
      }
    },

    // Renames files for browser caching purposes
    filerev: {
      dist: {
        src: [
          '<%= yeoman.dist %>/scripts/{,*/}*.js',
          '<%= yeoman.dist %>/styles/{,*/}*.css',
          '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
          '<%= yeoman.dist %>/styles/fonts/*'
        ]
      }
    },

    // Reads HTML for usemin blocks to enable smart builds that automatically
    // concat, minify and revision files. Creates configurations in memory so
    // additional tasks can operate on them
    useminPrepare: {
      html: '<%= yeoman.app %>/index.html',
      options: {
        dest: '<%= yeoman.dist %>',
        flow: {
          html: {
            steps: {
              js: ['concat', 'uglifyjs'],
              css: ['cssmin']
            },
            post: {}
          }
        }
      }
    },

    // Performs rewrites based on filerev and the useminPrepare configuration
    usemin: {
      html: ['<%= yeoman.dist %>/{,*/}*.html'],
      css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
      options: {
        assetsDirs: [
          '<%= yeoman.dist %>',
          '<%= yeoman.dist %>/images',
          '<%= yeoman.dist %>/styles'
        ]
      }
    },

    // The following *-min tasks will produce minified files in the dist folder
    // By default, your `index.html`'s <!-- Usemin block --> will take care of
    // minification. These next options are pre-configured if you do not wish
    // to use the Usemin blocks.
    cssmin: {
       dist: {
         files: {
           '<%= yeoman.dist %>/styles/main.css': [
             '.tmp/styles/{,*/}*.css'
           ]
         }
       }
     },
     uglify: {
       dist: {
         files: {
           '<%= yeoman.dist %>/scripts/scripts.js': [
             '<%= yeoman.dist %>/scripts/scripts.js'
           ]
         }
       }
     },
     concat: {
       dist: {}
     },

    imagemin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.app %>/images',
          src: '{,*/}*.{png,jpg,jpeg,gif}',
          dest: '<%= yeoman.dist %>/images'
        }]
      }
    },

    svgmin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.app %>/images',
          src: '{,*/}*.svg',
          dest: '<%= yeoman.dist %>/images'
        }]
      }
    },

    htmlmin: {
      dist: {
        options: {
          collapseWhitespace: true,
          conservativeCollapse: true,
          collapseBooleanAttributes: true,
          removeCommentsFromCDATA: true,
          removeOptionalTags: true
        },
        files: [{
          expand: true,
          cwd: '<%= yeoman.dist %>',
          src: ['*.html', '{,*/}*.html'],
          dest: '<%= yeoman.dist %>'
        }]
      }
    },

    // ng-annotate tries to make the code safe for minification automatically
    // by using the Angular long form for dependency injection.
    ngAnnotate: {
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/concat/scripts',
          src: '*.js',
          dest: '.tmp/concat/scripts'
        }]
      }
    },

    // Replace Google CDN references
    cdnify: {
      dist: {
        html: ['<%= yeoman.dist %>/*.html']
      }
    },

    // Copies remaining files to places other tasks can use
    copy: {
      dist: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%= yeoman.app %>',
          dest: '<%= yeoman.dist %>',
          src: [
            '*.{ico,png,txt}',
            '{,*/}*.html',
            'images/{,*/}*.{webp}',
            'styles/fonts/{,*/}*.*'
          ]
        }, {
          expand: true,
          cwd: '.tmp/images',
          dest: '<%= yeoman.dist %>/images',
          src: ['generated/*']
        }, {
          expand: true,
          cwd: 'bower_components/bootstrap/dist',
          src: 'fonts/*',
          dest: '<%= yeoman.dist %>'
        },{
          expand: true,
          cwd: 'bower_components/fontawesome',
          src: 'fonts/*',
          dest: '<%= yeoman.dist %>'
        }]
      },
      styles: {
        expand: true,
        cwd: '<%= yeoman.app %>/styles',
        dest: '.tmp/styles/',
        src: '{,*/}*.css'
      }
    },

    // Run some tasks in parallel to speed up the build process
    concurrent: {
      server: [
        'copy:styles'
      ],
      test: [
        'copy:styles'
      ],
      dist: [
        'copy:styles',
        'imagemin',
        'svgmin'
      ]
    },

    // Test settings
    karma: {
      unit: {
        configFile: 'test/karma.conf.js',
        singleRun: true
      }
    },

    // Execute Firebase Deploy

    exec: {
      deploy: {
        cmd: 'firebase deploy'
      },
      git_add: {
        cmd: 'git add .'
      },
      git_commit: {
        cmd: 'git commit -m "Latest Build and Firebase Deployment"'
      },
      git_push: {
        cmd: 'git push'
      }
    }

  });

  grunt.registerTask('serve', 'Compile then start a connect web server', function (target) {
    if (target === 'dist') {
      return grunt.task.run(['build', 'connect:dist:keepalive']);
    }

    grunt.task.run([
      'clean:server',
      'wiredep',
      'concurrent:server',
      'autoprefixer:server',
      'connect:livereload',
      'watch'
    ]);
  });

  grunt.registerTask('server', 'DEPRECATED TASK. Use the "serve" task instead', function (target) {
    grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
    grunt.task.run(['serve:' + target]);
  });

  grunt.registerTask('test', [
    'clean:server',
    'wiredep',
    'concurrent:test',
    'autoprefixer',
    'connect:test',
    'karma'
  ]);

  grunt.registerTask('build', [
    'clean:dist',
    'wiredep',
    'useminPrepare',
    'concurrent:dist',
    'autoprefixer',
    'concat',
    'ngAnnotate',
    'copy:dist',
    'cdnify',
    'cssmin',
    'uglify',
    'filerev',
    'usemin',
    'htmlmin'
  ]);

  grunt.registerTask('deploy', [
    'build',
    'exec:deploy',
    'exec:git_add',
    'exec:git_commit',
    "exec:git_push"
  ]);

  grunt.registerTask('default', [
    'newer:jshint',
    'test',
    'build'
  ]);
};
<|endoftext|>


<commit_before>module.exports = function(grunt) {
    "use strict";

    var path = require("path");
    var fs = require("fs");
    var execSync = require("child_process").execSync;
    var envify = require("envify/custom");

    var nunjucks = require("nunjucks");
    nunjucks.configure({watch: false});

    require("load-grunt-tasks")(grunt);

    var pkg = grunt.file.readJSON("package.json");

    // Get version number from last commit date.
    // Format: YY.MM.T (year.month.timestamp).
    var rev = execSync("git show -s --format='%cI %ct'").toString().trim().split(" ");
    pkg.version = rev[0].slice(2, 4) + "." + rev[0].slice(5, 7) + "." + rev[0].slice(8, 10) + "-" + rev[1];

    var buildConfig = grunt.file.readJSON("config.default.json");
    var buildConfigJson = grunt.option("config");

    if (buildConfigJson) {
        try {
            var customBuildConfig = grunt.file.readJSON(buildConfigJson);

            grunt.verbose.writeln("Using configuration from " + buildConfigJson);

            // Overwrite default config
            for (var key in customBuildConfig) {
                buildConfig[key] = customBuildConfig[key];
            }
        }
        catch (noBuildConfigFound) {
            grunt.log.error("Configuration file " + buildConfigJson + " not found.");
        }
    }

    // Remove duplicates from an array.
    function dedup(arr) {
        return arr.filter(function (x, pos) {
            return arr.indexOf(x) === pos;
        });
    }

    // Get the OS part from a platform identifier.
    function getPlatformOS(platform) {
        return platform.split("-")[0];
    }

    // Get the architecture part from a platform identifier.
    function getPlatformArch(platform) {
        return platform.split("-")[1];
    }

    grunt.initConfig({
        pkg: pkg,

        /*
         * Check JavaScript and CSS source files.
         */
        jshint: {
            options: {
                jshintrc: true
            },
            all: [ "js/**/*.js" ]
        },

        csslint: {
            options: {
                csslintrc: ".csslintrc"
            },
            all: [ "css/**/*.css" ]
        },

        /*
         * Automatic tests.
         */
        simplemocha: {
            options: {
                timeout: 3000,
                ignoreLeaks: false,
                ui: "bdd",
                reporter: "tap"
            },
            all: {
                src: ["test/**/*.mocha.js"]
            }
        },

        /*
         * Transpile JavaScript source files from ES6 to ES5
         */
        babel: {
            electron: {
                options: {
                    presets: [["@babel/preset-env", {targets: {node: "12"}}]]
                },
                files: [{
                    expand: true,
                    src: ["js/**/*.js"],
                    dest: "build/electron"
                }]
            },
            browser: {
                options: {
                    presets: ["@babel/preset-env"]
                },
                files: [{
                    expand: true,
                    src: ["js/**/*.js"],
                    dest: "build/browser"
                }]
            }
        },

        jspot: {
            options: {
                keyword: "_",
                parserOptions: {
                    sourceType: "module"
                }
            },
            all: {
                src: ["js/**/*.js"],
                dest: "locales"
            }
        },

        po2json: {
            options: {
                fuzzy: false,
                singleFile: true,
                nodeJs: true,
                format: "jed1.x"
            },
            electron: {
                src: ["locales/*.po"],
                dest: "build/electron/js/locales.js",
            },
            browser: {
                src: ["locales/*.po"],
                dest: "build/browser/js/locales.js",
            }
        },

        browserify: {
            editor: {
                options: {
                    external: ["electron", "fs", "process"],
                    browserifyOptions: {
                        basedir: "build/browser"
                    },
                    configure: b => b.transform({global: true}, envify({NODE_ENV: "production"}))
                },
                src: ["build/browser/js/editor.js"],
                dest: "build/tmp/js/editor.archive.js"
            },
            player: {
                src: ["build/browser/js/player.js"],
                dest: "build/tmp/js/player.archive.js"
            }
        },

        /*
         * Compress the JavaScript code of the editor, player, and presenter.
         */
        uglify: {
            editor: {
                src: "<%= browserify.editor.dest %>",
                dest: "build/browser/js/editor.min.js"
            },
            player: {
                src: "<%= browserify.player.dest %>",
                dest: "build/tmp/js/player.min.js"
            },
            presenter: {
                src: "build/browser/js/presenter.js",
                dest: "build/tmp/js/presenter.min.js"
            }
        },

        /*
         * Precompile templates for the editor and player.
         */
        nunjucks_render: {
            player: {
                src: "templates/player.html",
                dest: "build/browser/templates/player.html",
                context: {
                    playerJs: "<%= grunt.file.read('build/tmp/js/player.min.js') %>"
                }
            },
            presenter: {
                src: "templates/presenter.html",
                dest: "build/browser/templates/presenter.html",
                context: {
                    presenterJs: "<%= grunt.file.read('build/tmp/js/presenter.min.js') %>"
                }
            }
        },

        copy: {
            editor: {
                files: [
                    {
                        expand: true,
                        src: [
                            "index-electron.html",
                            "css/**/*",
                            "vendor/**/*"
                        ],
                        dest: "build/electron/"
                    },
                    {
                        expand: true,
                        src: [
                            "index-webapp.html",
                            "css/**/*",
                            "vendor/**/*"
                        ],
                        dest: "build/browser/"
                    },
                    {
                        expand: true,
                        flatten: true,
                        src: [
                            "<%= nunjucks_render.player.dest %>",
                            "<%= nunjucks_render.presenter.dest %>"
                        ],
                        dest: "build/electron/templates/"
                    }
                ]
            }
        },

        rename: {
            webapp_backend: {
                src: ["build/browser/js/backend/index-webapp.js"],
                dest: "build/browser/js/backend/index.js"
            },
            webapp_html: {
                src: ["build/browser/index-webapp.html"],
                dest: "build/browser/index.html"
            },
            electron_backend: {
                src: ["build/electron/js/backend/index-electron.js"],
                dest: "build/electron/js/backend/index.js"
            },
            electron_html: {
                src: ["build/electron/index-electron.html"],
                dest: "build/electron/index.html"
            }
        },

        compress: {
            media: {
                expand: true,
                cwd: "extras/media",
                src: ["**/*"],
                dest: "dist/Sozi-extras-media-<%= pkg.version %>.zip"
            }
        },

        "install-dependencies": {
            electron: {
                options: {
                    cwd: "build/electron"
                }
            },
            browser: {
                options: {
                    cwd: "build/browser"
                }
            }
        },

        electron: {
            editor: {
                options: {
                    name: "Sozi",
                    dir: "build/electron",
                    out: "dist",
                    overwrite: true,
                    prune: false,
                    electronVersion: buildConfig.electronVersion,
                    platform: dedup(buildConfig.platforms.map(getPlatformOS)).join(","),
                    arch:     dedup(buildConfig.platforms.map(getPlatformArch)).join(",")
                }
            }
        },

        /*
         * Upload the web demonstration of the Sozi editor.
         */
        rsync: {
            options: {
                args: ["--verbose", "--update", "--checksum"]
            },
            editor: {
                options: {
                    src: ["build/browser/*"],
                    dest: "/var/www/sozi.baierouge.fr/demo/",
                    host: "sozi@baierouge.fr",
                    deleteAll: true,
                    recursive: true
                }
            }
        },

        newer: {
            options: {
                override: function (details, include) {
                    if (details.task === "nunjucks_render") {
                        include(fs.statSync(`build/tmp/js/${details.target}.min.js`).mtime > details.time);
                    }
                    else {
                        include(false);
                    }
                }
            }
        }
    });

    /*
     * Generate installable archives for each platform.
     */

    var debianArchs = {
        ia32: "i386",
        x64: "amd64"
    };

    var renamedOS = {
        darwin: "osx",
        win32: "windows"
    };

    buildConfig.platforms.forEach(function (platform) {
        // The folder for the current platform.
        var distDir = "dist/Sozi-" + platform;
        // Get the components of the platform.
        var platformOS   = getPlatformOS(platform);
        if (platformOS in renamedOS) {
            platformOS = renamedOS[platformOS];
        }
        var platformArch = getPlatformArch(platform);
        // The name of the target folder for the current platform in dist/.
        var archiveName = "Sozi-" + pkg.version + "-" + platformOS + "-" + platformArch;
        // The renamed folder for the current platform.
        var archiveDir = "dist/" + archiveName;

        // Copy the installation assets for the target OS.
        grunt.config(["copy", platform], {
            options: {
                mode: true
            },
            files: [
                {
                    expand: true,
                    flatten: true,
                    src: "installation-assets/" + platformOS + "/*",
                    dest: distDir + "/install/"
                },
                {
                    src: "icons/icon-256.png",
                    dest: distDir + "/install/sozi.png"
                }
            ]
        });

        // Delete the distribution folder for this platform if it exists.
        grunt.config(["clean", platform], {
            src: archiveDir
        });

        // Rename the distribution folder to the archive name.
        grunt.config(["rename", platform], {
            src: distDir,
            dest: archiveDir
        });

        // Build zip files for Windows, tgz for other platforms.
        var archiveFormat = platformOS.startsWith("win") ? "zip" : "tar.xz";

        grunt.config(["compress", platform], {
            expand: true,
            cwd: "dist/",
            src: [archiveName + "/**/*"],
            dest: archiveDir + "." + archiveFormat
        });

        // Generate a Debian package for each Linux platform.
        if (platformOS === "linux" && platformArch in debianArchs) {
            grunt.config(["debian_package", platform], {
                version: pkg.version,
                platform: platform,
                arch: debianArchs[platformArch],
                date: new Date().toUTCString().slice(0, -3) + "+0000"
            });
        }
    });

    grunt.registerTask("copy-installation-assets", buildConfig.platforms.reduce(function (prev, platform) {
        var installationTask = buildConfig.installable.indexOf(getPlatformOS(platform)) >= 0 ? ["copy:" + platform] : [];
        return prev.concat(installationTask);
    }, []));

    grunt.registerTask("rename-platforms", buildConfig.platforms.reduce(function (prev, platform) {
        return prev.concat(["clean:" + platform, "rename:" + platform]);
    }, []));

    grunt.registerMultiTask("compress", function () {
        this.files.forEach(function (file) {
            if (file.dest.endsWith(".tar.xz")) {
                execSync("tar cJf " + file.dest + " " + files.src[0]);
            }
            else if (file.dest.endsWith(".zip")) {
                execSync("zip -ry " + file.dest + " " + files.src[0]);
            }
        });
    });

    grunt.registerTask("compress-platforms", buildConfig.platforms.reduce(function (prev, platform) {
        return prev.concat(["compress:" + platform]);
    }, []));

    grunt.registerTask("write_package_json", function () {
        grunt.file.write("build/electron/package.json", JSON.stringify(pkg));
        grunt.file.write("build/browser/package.json", JSON.stringify(pkg));
    });

    grunt.registerMultiTask("nunjucks_render", function () {
        this.files.forEach(function (file) {
            grunt.file.write(file.dest, nunjucks.render(file.src[0], this.data.context));
            grunt.log.writeln("File " + file.dest + " created.");
        }, this);
    });

    grunt.registerMultiTask("debian_package", function () {
        var workDir = "dist/packaging-" + this.data.platform;
        grunt.file.write(workDir + "/Makefile",  nunjucks.render("debian/Makefile", this.data));
        grunt.file.write(workDir + "/debian/changelog", nunjucks.render("debian/changelog", this.data));
        grunt.file.write(workDir + "/debian/compat", nunjucks.render("debian/compat", this.data));
        grunt.file.write(workDir + "/debian/control", nunjucks.render("debian/control", this.data));
        grunt.file.write(workDir + "/debian/links", nunjucks.render("debian/links", this.data));
        grunt.file.write(workDir + "/debian/rules", nunjucks.render("debian/rules", this.data));
        execSync("dpkg-buildpackage -a" + this.data.arch, {cwd: workDir});
    });

    grunt.registerTask("lint", ["jshint", "csslint"]);

    grunt.registerTask("build", [
        "write_package_json",
        "newer:babel",
        "browserify:player", // Cannot use 'newer' here due to imports
        "newer:uglify:player",
        "newer:uglify:presenter",
        "newer:nunjucks_render",
        "newer:po2json",
        "newer:copy:editor",
        "compress:media"
    ]);

    grunt.registerTask("electron-build",  [
        "build",
        "rename:electron_backend",
        "rename:electron_html",
        "install-dependencies",
        "electron",
        "copy-installation-assets"
    ]);

    grunt.registerTask("web-build", [
        "build",
        "rename:webapp_backend",
        "rename:webapp_html",
        "browserify:editor", // Cannot use 'newer' here due to imports
        "newer:uglify:editor"
    ]);

    grunt.registerTask("electron-archive", [
        "electron-build",
        "rename-platforms",
        "compress-platforms"
    ]);

    grunt.registerTask("web-demo", [
        "web-build",
        "rsync" // Cannot use 'newer' here since 'dest' is not a generated file
    ]);

    grunt.registerTask("pot", [
        "newer:babel",
        "jspot" // Cannot use 'newer' here since 'dest' is not a generated file
    ]);

    if (buildConfig.platforms.some(p => getPlatformOS(p) === "linux")) {
        grunt.registerTask("deb", [
            "electron-build",
            "rename-platforms",
            "debian_package"
        ]);

        grunt.registerTask("dist", [
            "electron-archive",
            "debian_package"
        ]);
    }
    else {
        grunt.registerTask("dist", ["electron-archive"]);
    }

    grunt.registerTask("default", ["electron-archive"]);
};
<commit_msg>Fixed compress task<commit_after>module.exports = function(grunt) {
    "use strict";

    var path = require("path");
    var fs = require("fs");
    var execSync = require("child_process").execSync;
    var envify = require("envify/custom");

    var nunjucks = require("nunjucks");
    nunjucks.configure({watch: false});

    require("load-grunt-tasks")(grunt);

    var pkg = grunt.file.readJSON("package.json");

    // Get version number from last commit date.
    // Format: YY.MM.T (year.month.timestamp).
    var rev = execSync("git show -s --format='%cI %ct'").toString().trim().split(" ");
    pkg.version = rev[0].slice(2, 4) + "." + rev[0].slice(5, 7) + "." + rev[0].slice(8, 10) + "-" + rev[1];

    var buildConfig = grunt.file.readJSON("config.default.json");
    var buildConfigJson = grunt.option("config");

    if (buildConfigJson) {
        try {
            var customBuildConfig = grunt.file.readJSON(buildConfigJson);

            grunt.verbose.writeln("Using configuration from " + buildConfigJson);

            // Overwrite default config
            for (var key in customBuildConfig) {
                buildConfig[key] = customBuildConfig[key];
            }
        }
        catch (noBuildConfigFound) {
            grunt.log.error("Configuration file " + buildConfigJson + " not found.");
        }
    }

    // Remove duplicates from an array.
    function dedup(arr) {
        return arr.filter(function (x, pos) {
            return arr.indexOf(x) === pos;
        });
    }

    // Get the OS part from a platform identifier.
    function getPlatformOS(platform) {
        return platform.split("-")[0];
    }

    // Get the architecture part from a platform identifier.
    function getPlatformArch(platform) {
        return platform.split("-")[1];
    }

    grunt.initConfig({
        pkg: pkg,

        /*
         * Check JavaScript and CSS source files.
         */
        jshint: {
            options: {
                jshintrc: true
            },
            all: [ "js/**/*.js" ]
        },

        csslint: {
            options: {
                csslintrc: ".csslintrc"
            },
            all: [ "css/**/*.css" ]
        },

        /*
         * Automatic tests.
         */
        simplemocha: {
            options: {
                timeout: 3000,
                ignoreLeaks: false,
                ui: "bdd",
                reporter: "tap"
            },
            all: {
                src: ["test/**/*.mocha.js"]
            }
        },

        /*
         * Transpile JavaScript source files from ES6 to ES5
         */
        babel: {
            electron: {
                options: {
                    presets: [["@babel/preset-env", {targets: {node: "12"}}]]
                },
                files: [{
                    expand: true,
                    src: ["js/**/*.js"],
                    dest: "build/electron"
                }]
            },
            browser: {
                options: {
                    presets: ["@babel/preset-env"]
                },
                files: [{
                    expand: true,
                    src: ["js/**/*.js"],
                    dest: "build/browser"
                }]
            }
        },

        jspot: {
            options: {
                keyword: "_",
                parserOptions: {
                    sourceType: "module"
                }
            },
            all: {
                src: ["js/**/*.js"],
                dest: "locales"
            }
        },

        po2json: {
            options: {
                fuzzy: false,
                singleFile: true,
                nodeJs: true,
                format: "jed1.x"
            },
            electron: {
                src: ["locales/*.po"],
                dest: "build/electron/js/locales.js",
            },
            browser: {
                src: ["locales/*.po"],
                dest: "build/browser/js/locales.js",
            }
        },

        browserify: {
            editor: {
                options: {
                    external: ["electron", "fs", "process"],
                    browserifyOptions: {
                        basedir: "build/browser"
                    },
                    configure: b => b.transform({global: true}, envify({NODE_ENV: "production"}))
                },
                src: ["build/browser/js/editor.js"],
                dest: "build/tmp/js/editor.archive.js"
            },
            player: {
                src: ["build/browser/js/player.js"],
                dest: "build/tmp/js/player.archive.js"
            }
        },

        /*
         * Compress the JavaScript code of the editor, player, and presenter.
         */
        uglify: {
            editor: {
                src: "<%= browserify.editor.dest %>",
                dest: "build/browser/js/editor.min.js"
            },
            player: {
                src: "<%= browserify.player.dest %>",
                dest: "build/tmp/js/player.min.js"
            },
            presenter: {
                src: "build/browser/js/presenter.js",
                dest: "build/tmp/js/presenter.min.js"
            }
        },

        /*
         * Precompile templates for the editor and player.
         */
        nunjucks_render: {
            player: {
                src: "templates/player.html",
                dest: "build/browser/templates/player.html",
                context: {
                    playerJs: "<%= grunt.file.read('build/tmp/js/player.min.js') %>"
                }
            },
            presenter: {
                src: "templates/presenter.html",
                dest: "build/browser/templates/presenter.html",
                context: {
                    presenterJs: "<%= grunt.file.read('build/tmp/js/presenter.min.js') %>"
                }
            }
        },

        copy: {
            editor: {
                files: [
                    {
                        expand: true,
                        src: [
                            "index-electron.html",
                            "css/**/*",
                            "vendor/**/*"
                        ],
                        dest: "build/electron/"
                    },
                    {
                        expand: true,
                        src: [
                            "index-webapp.html",
                            "css/**/*",
                            "vendor/**/*"
                        ],
                        dest: "build/browser/"
                    },
                    {
                        expand: true,
                        flatten: true,
                        src: [
                            "<%= nunjucks_render.player.dest %>",
                            "<%= nunjucks_render.presenter.dest %>"
                        ],
                        dest: "build/electron/templates/"
                    }
                ]
            }
        },

        rename: {
            webapp_backend: {
                src: ["build/browser/js/backend/index-webapp.js"],
                dest: "build/browser/js/backend/index.js"
            },
            webapp_html: {
                src: ["build/browser/index-webapp.html"],
                dest: "build/browser/index.html"
            },
            electron_backend: {
                src: ["build/electron/js/backend/index-electron.js"],
                dest: "build/electron/js/backend/index.js"
            },
            electron_html: {
                src: ["build/electron/index-electron.html"],
                dest: "build/electron/index.html"
            }
        },

        compress: {
            media: {
                options: {
                    archive: "dist/Sozi-extras-media-<%= pkg.version %>.zip",
                    cwd: "extras/media"
                },
                src: [
                    "extras/media/sozi_extras_media.inx",
                    "extras/media/sozi_extras_media.py"
                ]
            }
        },

        "install-dependencies": {
            electron: {
                options: {
                    cwd: "build/electron"
                }
            },
            browser: {
                options: {
                    cwd: "build/browser"
                }
            }
        },

        electron: {
            editor: {
                options: {
                    name: "Sozi",
                    dir: "build/electron",
                    out: "dist",
                    overwrite: true,
                    prune: false,
                    electronVersion: buildConfig.electronVersion,
                    platform: dedup(buildConfig.platforms.map(getPlatformOS)).join(","),
                    arch:     dedup(buildConfig.platforms.map(getPlatformArch)).join(",")
                }
            }
        },

        /*
         * Upload the web demonstration of the Sozi editor.
         */
        rsync: {
            options: {
                args: ["--verbose", "--update", "--checksum"]
            },
            editor: {
                options: {
                    src: ["build/browser/*"],
                    dest: "/var/www/sozi.baierouge.fr/demo/",
                    host: "sozi@baierouge.fr",
                    deleteAll: true,
                    recursive: true
                }
            }
        },

        newer: {
            options: {
                override: function (details, include) {
                    if (details.task === "nunjucks_render") {
                        include(fs.statSync(`build/tmp/js/${details.target}.min.js`).mtime > details.time);
                    }
                    else {
                        include(false);
                    }
                }
            }
        }
    });

    /*
     * Generate installable archives for each platform.
     */

    var debianArchs = {
        ia32: "i386",
        x64: "amd64"
    };

    var renamedOS = {
        darwin: "osx",
        win32: "windows"
    };

    buildConfig.platforms.forEach(function (platform) {
        // The folder for the current platform.
        var distDir = "dist/Sozi-" + platform;
        // Get the components of the platform.
        var platformOS   = getPlatformOS(platform);
        if (platformOS in renamedOS) {
            platformOS = renamedOS[platformOS];
        }
        var platformArch = getPlatformArch(platform);
        // The name of the target folder for the current platform in dist/.
        var archiveName = "Sozi-" + pkg.version + "-" + platformOS + "-" + platformArch;
        // The renamed folder for the current platform.
        var archiveDir = "dist/" + archiveName;

        // Copy the installation assets for the target OS.
        grunt.config(["copy", platform], {
            options: {
                mode: true
            },
            files: [
                {
                    expand: true,
                    flatten: true,
                    src: "installation-assets/" + platformOS + "/*",
                    dest: distDir + "/install/"
                },
                {
                    src: "icons/icon-256.png",
                    dest: distDir + "/install/sozi.png"
                }
            ]
        });

        // Delete the distribution folder for this platform if it exists.
        grunt.config(["clean", platform], {
            src: archiveDir
        });

        // Rename the distribution folder to the archive name.
        grunt.config(["rename", platform], {
            src: distDir,
            dest: archiveDir
        });

        // Build zip files for Windows, tgz for other platforms.
        var archiveFormat = platformOS.startsWith("win") ? "zip" : "tar.xz";

        grunt.config(["compress", platform], {
            options: {
                archive: archiveDir + "." + archiveFormat,
                cwd: "dist/"
            },
            src: [archiveDir]
        });

        // Generate a Debian package for each Linux platform.
        if (platformOS === "linux" && platformArch in debianArchs) {
            grunt.config(["debian_package", platform], {
                version: pkg.version,
                platform: platform,
                arch: debianArchs[platformArch],
                date: new Date().toUTCString().slice(0, -3) + "+0000"
            });
        }
    });

    grunt.registerTask("copy-installation-assets", buildConfig.platforms.reduce(function (prev, platform) {
        var installationTask = buildConfig.installable.indexOf(getPlatformOS(platform)) >= 0 ? ["copy:" + platform] : [];
        return prev.concat(installationTask);
    }, []));

    grunt.registerTask("rename-platforms", buildConfig.platforms.reduce(function (prev, platform) {
        return prev.concat(["clean:" + platform, "rename:" + platform]);
    }, []));

    grunt.registerMultiTask("compress", function () {
        const dest = this.options().archive;
        const cwd = this.options().cwd;
        const src = this.files.map(f => f.src).flat().map(p => path.relative(cwd, p)).join(" ");

        grunt.log.writeln("Compressing " + dest);
        if (dest.endsWith(".tar.xz")) {
            execSync("tar cJf " + " " + dest + " -C " + cwd + " " + src);
        }
        else if (dest.endsWith(".zip")) {
            execSync("zip -y " + path.relative(cwd, dest) + " " + src, {cwd});
        }
    });

    grunt.registerTask("compress-platforms", buildConfig.platforms.reduce(function (prev, platform) {
        return prev.concat(["compress:" + platform]);
    }, []));

    grunt.registerTask("write_package_json", function () {
        grunt.file.write("build/electron/package.json", JSON.stringify(pkg));
        grunt.file.write("build/browser/package.json", JSON.stringify(pkg));
    });

    grunt.registerMultiTask("nunjucks_render", function () {
        this.files.forEach(function (file) {
            grunt.file.write(file.dest, nunjucks.render(file.src[0], this.data.context));
            grunt.log.writeln("File " + file.dest + " created.");
        }, this);
    });

    grunt.registerMultiTask("debian_package", function () {
        var workDir = "dist/packaging-" + this.data.platform;
        grunt.file.write(workDir + "/Makefile",  nunjucks.render("debian/Makefile", this.data));
        grunt.file.write(workDir + "/debian/changelog", nunjucks.render("debian/changelog", this.data));
        grunt.file.write(workDir + "/debian/compat", nunjucks.render("debian/compat", this.data));
        grunt.file.write(workDir + "/debian/control", nunjucks.render("debian/control", this.data));
        grunt.file.write(workDir + "/debian/links", nunjucks.render("debian/links", this.data));
        grunt.file.write(workDir + "/debian/rules", nunjucks.render("debian/rules", this.data));
        execSync("dpkg-buildpackage -a" + this.data.arch, {cwd: workDir});
    });

    grunt.registerTask("lint", ["jshint", "csslint"]);

    grunt.registerTask("build", [
        "write_package_json",
        "newer:babel",
        "browserify:player", // Cannot use 'newer' here due to imports
        "newer:uglify:player",
        "newer:uglify:presenter",
        "newer:nunjucks_render",
        "newer:po2json",
        "newer:copy:editor",
        "compress:media"
    ]);

    grunt.registerTask("electron-build",  [
        "build",
        "rename:electron_backend",
        "rename:electron_html",
        "install-dependencies",
        "electron",
        "copy-installation-assets"
    ]);

    grunt.registerTask("web-build", [
        "build",
        "rename:webapp_backend",
        "rename:webapp_html",
        "browserify:editor", // Cannot use 'newer' here due to imports
        "newer:uglify:editor"
    ]);

    grunt.registerTask("electron-archive", [
        "electron-build",
        "rename-platforms",
        "compress-platforms"
    ]);

    grunt.registerTask("web-demo", [
        "web-build",
        "rsync" // Cannot use 'newer' here since 'dest' is not a generated file
    ]);

    grunt.registerTask("pot", [
        "newer:babel",
        "jspot" // Cannot use 'newer' here since 'dest' is not a generated file
    ]);

    if (buildConfig.platforms.some(p => getPlatformOS(p) === "linux")) {
        grunt.registerTask("deb", [
            "electron-build",
            "rename-platforms",
            "debian_package"
        ]);

        grunt.registerTask("dist", [
            "electron-archive",
            "debian_package"
        ]);
    }
    else {
        grunt.registerTask("dist", ["electron-archive"]);
    }

    grunt.registerTask("default", ["electron-archive"]);
};
<|endoftext|>


<commit_before>'use strict';

module.exports = function(grunt) {
  var path = require('path');
  var os = require('os');
  var through = require('through2');
  var proxyquire = require('proxyquireify');
  var versionify = require('browserify-versionify');
  var derequire = require('derequire/plugin');
  var collapser = require('bundle-collapser/plugin');

  var excludedPlugins = ['react-native'];

  var plugins = grunt.option('plugins');
  // Create plugin paths and verify they exist
  plugins = (plugins ? plugins.split(',') : []).map(function(plugin) {
    var p = 'plugins/' + plugin + '.js';

    if (!grunt.file.exists(p))
      throw new Error("Plugin '" + plugin + "' not found in plugins directory.");

    return p;
  });

  // custom browserify transformer to re-write plugins to
  // self-register with Raven via addPlugin
  function AddPluginBrowserifyTransformer() {
    return function(file) {
      return through(function(buf, enc, next) {
        buf = buf.toString('utf8');
        if (/plugins/.test(file)) {
          buf += "\nrequire('../src/singleton').addPlugin(module.exports);";
        }
        this.push(buf);
        next();
      });
    };
  }

  // Taken from http://dzone.com/snippets/calculate-all-combinations
  var combine = function(a) {
    var fn = function(n, src, got, all) {
      if (n === 0) {
        all.push(got);
        return;
      }

      for (var j = 0; j < src.length; j++) {
        fn(n - 1, src.slice(j + 1), got.concat([src[j]]), all);
      }
    };

    var excluded = excludedPlugins.map(function(plugin) {
      return 'plugins/' + plugin + '.js';
    });

    // Remove the plugins that we don't want to build
    a = a.filter(function(n) {
      return excluded.indexOf(n) === -1;
    });

    var all = [a];

    for (var i = 0; i < a.length; i++) {
      fn(i, a, [], all);
    }

    return all;
  };

  var plugins = grunt.file.expand('plugins/*.js');

  var cleanedPlugins = plugins.filter(function(plugin) {
    var pluginName = path.basename(plugin, '.js');

    return excludedPlugins.indexOf(pluginName) === -1;
  });

  var pluginSingleFiles = cleanedPlugins.map(function(plugin) {
    var filename = path.basename(plugin);

    var file = {};
    file.src = plugin;
    file.dest = path.join('build', 'plugins', filename);

    return file;
  });

  var pluginCombinations = combine(plugins);
  var pluginConcatFiles = pluginCombinations.reduce(function(dict, comb) {
    var key = comb.map(function(plugin) {
      return path.basename(plugin, '.js');
    });
    key.sort();

    var dest = path.join('build/', key.join(','), '/raven.js');
    dict[dest] = ['src/singleton.js'].concat(comb);

    return dict;
  }, {});

  var browserifyConfig = {
    options: {
      banner: grunt.file.read('template/_copyright.js'),
      browserifyOptions: {
        standalone: 'Raven' // umd
      },
      transform: [versionify],
      plugin: [derequire, collapser]
    },
    core: {
      src: 'src/singleton.js',
      dest: 'build/raven.js'
    },
    'plugins-combined': {
      files: pluginConcatFiles,
      options: {
        transform: [[versionify], [new AddPluginBrowserifyTransformer()]]
      }
    },
    test: {
      src: 'test/**/*.test.js',
      dest: 'build/raven.test.js',
      options: {
        browserifyOptions: {
          debug: false // source maps
        },
        ignore: ['react-native'],
        plugin: [proxyquire.plugin]
      }
    }
  };

  // Create a dedicated entry in browserify config for
  // each individual plugin (each needs a unique `standalone`
  // config)
  var browserifyPluginTaskNames = [];
  pluginSingleFiles.forEach(function(item) {
    var name = item.src
      .replace(/.*\//, '') // everything before slash
      .replace('.js', ''); // extension
    var capsName = name.charAt(0).toUpperCase() + name.slice(1);
    var config = {
      src: item.src,
      dest: item.dest,
      options: {
        browserifyOptions: {
          // e.g. Raven.Plugins.Angular
          standalone: 'Raven.Plugins.' + capsName
        }
      }
    };
    browserifyConfig[name] = config;
    browserifyPluginTaskNames.push('browserify:' + name);
  });

  var awsConfigPath = path.join(os.homedir(), '.aws', 'raven-js.json');
  var gruntConfig = {
    pkg: grunt.file.readJSON('package.json'),
    aws: grunt.file.exists(awsConfigPath) ? grunt.file.readJSON(awsConfigPath) : {},

    clean: ['build'],

    browserify: browserifyConfig,

    uglify: {
      options: {
        sourceMap: true,

        // Only preserve comments that start with (!)
        preserveComments: /^!/,

        // Minify object properties that begin with _ ("private"
        // methods and values)
        mangleProperties: {
          regex: /^_/
        },

        compress: {
          booleans: true,
          conditionals: true,
          dead_code: true,
          join_vars: true,
          pure_getters: true,
          sequences: true,
          unused: true,

          global_defs: {
            __DEV__: false
          }
        }
      },
      dist: {
        src: ['build/**/*.js'],
        ext: '.min.js',
        expand: true
      }
    },

    'saucelabs-mocha': {
      all: {
        options: {
          urls: [
            'http://127.0.0.1:9999/test/index.html',
            'http://127.0.0.1:9999/test/integration/index.html'
          ],
          sauceConfig: {
            'record-video': false,
            'record-screenshots': false
          },
          build: process.env.TRAVIS_BUILD_NUMBER,
          testname:
            'Raven.js' +
            (process.env.TRAVIS_JOB_NUMBER ? ' #' + process.env.TRAVIS_JOB_NUMBER : ''),
          browsers: [
            // Latest version of Edge (v15) can't reach the server
            // Already reached SauceLabs support
            // ['Windows 10', 'microsoftedge', 'latest'],

            // Commenting out until we fix initial issues
            // ['Windows 10', 'microsoftedge', 'latest-1'],
            // ['Windows 10', 'internet explorer', '11'],

            ['Windows 10', 'chrome', 'latest'],
            ['Windows 10', 'chrome', 'latest-1'],
            ['Windows 10', 'firefox', 'latest'],
            ['Windows 10', 'firefox', 'latest-1'],
            ['macOS 10.12', 'safari', '10'],
            ['macOS 10.12', 'chrome', 'latest'],
            ['macOS 10.12', 'chrome', 'latest-1'],
            ['macOS 10.12', 'firefox', 'latest'][('macOS 10.12', 'firefox', 'latest-1')]
          ],
          public: 'public',
          tunnelArgs: ['--verbose']
        }
      }
    },

    release: {
      options: {
        npm: false,
        commitMessage: 'Release <%= version %>'
      }
    },

    s3: {
      options: {
        key: '<%= aws.key %>',
        secret: '<%= aws.secret %>',
        bucket: '<%= aws.bucket %>',
        access: 'public-read',
        // Limit concurrency
        maxOperations: 20,
        headers: {
          // Surrogate-Key header for Fastly to purge by release
          'x-amz-meta-surrogate-key': '<%= pkg.release %>'
        }
      },
      all: {
        upload: [
          {
            src: 'build/**/*',
            dest: '<%= pkg.release %>/',
            rel: 'build/'
          }
        ]
      }
    },

    connect: {
      ci: {
        options: {
          port: 9999
        }
      },

      test: {
        options: {
          port: 8000,
          debug: true,
          keepalive: true
        }
      },

      docs: {
        options: {
          port: 8000,
          debug: true,
          base: 'docs/_build/html',
          keepalive: true
        }
      }
    },

    copy: {
      dist: {
        expand: true,
        flatten: false,
        cwd: 'build/',
        src: '**',
        dest: 'dist/'
      }
    },

    sri: {
      dist: {
        src: ['dist/*.js'],
        options: {
          dest: 'dist/sri.json',
          pretty: true
        }
      },
      build: {
        src: ['build/**/*.js'],
        options: {
          dest: 'build/sri.json',
          pretty: true
        }
      }
    }
  };

  grunt.initConfig(gruntConfig);

  // Custom Grunt tasks
  grunt.registerTask('version', function() {
    var pkg = grunt.config.get('pkg');

    // Verify version string in source code matches what's in package.json
    var Raven = require('./src/raven');
    if (Raven.prototype.VERSION !== pkg.version) {
      return grunt.util.error(
        'Mismatched version in src/raven.js: ' +
          Raven.prototype.VERSION +
          ' (should be ' +
          pkg.version +
          ')'
      );
    }

    if (grunt.option('dev')) {
      pkg.release = 'dev';
    } else {
      pkg.release = pkg.version;
    }
    grunt.config.set('pkg', pkg);
  });

  grunt.registerTask('config:ci', 'Verify CI config', function() {
    if (!process.env.SAUCE_USERNAME)
      console.warn('No SAUCE_USERNAME env variable defined.');
    if (!process.env.SAUCE_ACCESS_KEY)
      console.warn('No SAUCE_ACCESS_KEY env variable defined.');
    if (!process.env.SAUCE_USERNAME || !process.env.SAUCE_ACCESS_KEY) process.exit(1);
  });

  // Grunt contrib tasks
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-connect');
  grunt.loadNpmTasks('grunt-contrib-copy');

  // 3rd party Grunt tasks
  grunt.loadNpmTasks('grunt-browserify');
  grunt.loadNpmTasks('grunt-release');
  grunt.loadNpmTasks('grunt-s3');
  grunt.loadNpmTasks('grunt-gitinfo');
  grunt.loadNpmTasks('grunt-sri');
  grunt.loadNpmTasks('grunt-saucelabs');

  // Build tasks
  grunt.registerTask('_prep', ['clean', 'gitinfo', 'version']);
  grunt.registerTask(
    'browserify.core',
    ['_prep', 'browserify:core'].concat(browserifyPluginTaskNames)
  );
  grunt.registerTask('browserify.plugins-combined', [
    '_prep',
    'browserify:plugins-combined'
  ]);
  grunt.registerTask('build.test', ['_prep', 'browserify.core', 'browserify:test']);
  grunt.registerTask('build.core', ['browserify.core', 'uglify', 'sri:dist']);
  grunt.registerTask('build.plugins-combined', [
    'browserify.plugins-combined',
    'uglify',
    'sri:dist',
    'sri:build'
  ]);
  grunt.registerTask('build', ['build.plugins-combined']);
  grunt.registerTask('dist', ['build.core', 'copy:dist']);

  grunt.registerTask('test:ci', [
    'config:ci',
    'build.test',
    'connect:ci',
    'saucelabs-mocha'
  ]);

  // Webserver tasks
  grunt.registerTask('run:test', ['connect:test']);
  grunt.registerTask('run:docs', ['connect:docs']);

  grunt.registerTask('publish', ['build.plugins-combined', 's3']);
};
<commit_msg>ci: Fixed browsers config for SauceLabs<commit_after>'use strict';

module.exports = function(grunt) {
  var path = require('path');
  var os = require('os');
  var through = require('through2');
  var proxyquire = require('proxyquireify');
  var versionify = require('browserify-versionify');
  var derequire = require('derequire/plugin');
  var collapser = require('bundle-collapser/plugin');

  var excludedPlugins = ['react-native'];

  var plugins = grunt.option('plugins');
  // Create plugin paths and verify they exist
  plugins = (plugins ? plugins.split(',') : []).map(function(plugin) {
    var p = 'plugins/' + plugin + '.js';

    if (!grunt.file.exists(p))
      throw new Error("Plugin '" + plugin + "' not found in plugins directory.");

    return p;
  });

  // custom browserify transformer to re-write plugins to
  // self-register with Raven via addPlugin
  function AddPluginBrowserifyTransformer() {
    return function(file) {
      return through(function(buf, enc, next) {
        buf = buf.toString('utf8');
        if (/plugins/.test(file)) {
          buf += "\nrequire('../src/singleton').addPlugin(module.exports);";
        }
        this.push(buf);
        next();
      });
    };
  }

  // Taken from http://dzone.com/snippets/calculate-all-combinations
  var combine = function(a) {
    var fn = function(n, src, got, all) {
      if (n === 0) {
        all.push(got);
        return;
      }

      for (var j = 0; j < src.length; j++) {
        fn(n - 1, src.slice(j + 1), got.concat([src[j]]), all);
      }
    };

    var excluded = excludedPlugins.map(function(plugin) {
      return 'plugins/' + plugin + '.js';
    });

    // Remove the plugins that we don't want to build
    a = a.filter(function(n) {
      return excluded.indexOf(n) === -1;
    });

    var all = [a];

    for (var i = 0; i < a.length; i++) {
      fn(i, a, [], all);
    }

    return all;
  };

  var plugins = grunt.file.expand('plugins/*.js');

  var cleanedPlugins = plugins.filter(function(plugin) {
    var pluginName = path.basename(plugin, '.js');

    return excludedPlugins.indexOf(pluginName) === -1;
  });

  var pluginSingleFiles = cleanedPlugins.map(function(plugin) {
    var filename = path.basename(plugin);

    var file = {};
    file.src = plugin;
    file.dest = path.join('build', 'plugins', filename);

    return file;
  });

  var pluginCombinations = combine(plugins);
  var pluginConcatFiles = pluginCombinations.reduce(function(dict, comb) {
    var key = comb.map(function(plugin) {
      return path.basename(plugin, '.js');
    });
    key.sort();

    var dest = path.join('build/', key.join(','), '/raven.js');
    dict[dest] = ['src/singleton.js'].concat(comb);

    return dict;
  }, {});

  var browserifyConfig = {
    options: {
      banner: grunt.file.read('template/_copyright.js'),
      browserifyOptions: {
        standalone: 'Raven' // umd
      },
      transform: [versionify],
      plugin: [derequire, collapser]
    },
    core: {
      src: 'src/singleton.js',
      dest: 'build/raven.js'
    },
    'plugins-combined': {
      files: pluginConcatFiles,
      options: {
        transform: [[versionify], [new AddPluginBrowserifyTransformer()]]
      }
    },
    test: {
      src: 'test/**/*.test.js',
      dest: 'build/raven.test.js',
      options: {
        browserifyOptions: {
          debug: false // source maps
        },
        ignore: ['react-native'],
        plugin: [proxyquire.plugin]
      }
    }
  };

  // Create a dedicated entry in browserify config for
  // each individual plugin (each needs a unique `standalone`
  // config)
  var browserifyPluginTaskNames = [];
  pluginSingleFiles.forEach(function(item) {
    var name = item.src
      .replace(/.*\//, '') // everything before slash
      .replace('.js', ''); // extension
    var capsName = name.charAt(0).toUpperCase() + name.slice(1);
    var config = {
      src: item.src,
      dest: item.dest,
      options: {
        browserifyOptions: {
          // e.g. Raven.Plugins.Angular
          standalone: 'Raven.Plugins.' + capsName
        }
      }
    };
    browserifyConfig[name] = config;
    browserifyPluginTaskNames.push('browserify:' + name);
  });

  var awsConfigPath = path.join(os.homedir(), '.aws', 'raven-js.json');
  var gruntConfig = {
    pkg: grunt.file.readJSON('package.json'),
    aws: grunt.file.exists(awsConfigPath) ? grunt.file.readJSON(awsConfigPath) : {},

    clean: ['build'],

    browserify: browserifyConfig,

    uglify: {
      options: {
        sourceMap: true,

        // Only preserve comments that start with (!)
        preserveComments: /^!/,

        // Minify object properties that begin with _ ("private"
        // methods and values)
        mangleProperties: {
          regex: /^_/
        },

        compress: {
          booleans: true,
          conditionals: true,
          dead_code: true,
          join_vars: true,
          pure_getters: true,
          sequences: true,
          unused: true,

          global_defs: {
            __DEV__: false
          }
        }
      },
      dist: {
        src: ['build/**/*.js'],
        ext: '.min.js',
        expand: true
      }
    },

    'saucelabs-mocha': {
      all: {
        options: {
          urls: [
            'http://127.0.0.1:9999/test/index.html',
            'http://127.0.0.1:9999/test/integration/index.html'
          ],
          sauceConfig: {
            'record-video': false,
            'record-screenshots': false
          },
          build: process.env.TRAVIS_BUILD_NUMBER,
          testname:
            'Raven.js' +
            (process.env.TRAVIS_JOB_NUMBER ? ' #' + process.env.TRAVIS_JOB_NUMBER : ''),
          browsers: [
            // Latest version of Edge (v15) can't reach the server
            // Already reached SauceLabs support
            // ['Windows 10', 'microsoftedge', 'latest'],

            // Commenting out until we fix initial issues
            // ['Windows 10', 'microsoftedge', 'latest-1'],
            // ['Windows 10', 'internet explorer', '11'],

            ['Windows 10', 'chrome', 'latest'],
            ['Windows 10', 'chrome', 'latest-1'],
            ['Windows 10', 'firefox', 'latest'],
            ['Windows 10', 'firefox', 'latest-1'],
            ['macOS 10.12', 'safari', '10'],
            ['macOS 10.12', 'chrome', 'latest'],
            ['macOS 10.12', 'chrome', 'latest-1'],
            ['macOS 10.12', 'firefox', 'latest'],
            ['macOS 10.12', 'firefox', 'latest-1']
          ],
          public: 'public',
          tunnelArgs: ['--verbose']
        }
      }
    },

    release: {
      options: {
        npm: false,
        commitMessage: 'Release <%= version %>'
      }
    },

    s3: {
      options: {
        key: '<%= aws.key %>',
        secret: '<%= aws.secret %>',
        bucket: '<%= aws.bucket %>',
        access: 'public-read',
        // Limit concurrency
        maxOperations: 20,
        headers: {
          // Surrogate-Key header for Fastly to purge by release
          'x-amz-meta-surrogate-key': '<%= pkg.release %>'
        }
      },
      all: {
        upload: [
          {
            src: 'build/**/*',
            dest: '<%= pkg.release %>/',
            rel: 'build/'
          }
        ]
      }
    },

    connect: {
      ci: {
        options: {
          port: 9999
        }
      },

      test: {
        options: {
          port: 8000,
          debug: true,
          keepalive: true
        }
      },

      docs: {
        options: {
          port: 8000,
          debug: true,
          base: 'docs/_build/html',
          keepalive: true
        }
      }
    },

    copy: {
      dist: {
        expand: true,
        flatten: false,
        cwd: 'build/',
        src: '**',
        dest: 'dist/'
      }
    },

    sri: {
      dist: {
        src: ['dist/*.js'],
        options: {
          dest: 'dist/sri.json',
          pretty: true
        }
      },
      build: {
        src: ['build/**/*.js'],
        options: {
          dest: 'build/sri.json',
          pretty: true
        }
      }
    }
  };

  grunt.initConfig(gruntConfig);

  // Custom Grunt tasks
  grunt.registerTask('version', function() {
    var pkg = grunt.config.get('pkg');

    // Verify version string in source code matches what's in package.json
    var Raven = require('./src/raven');
    if (Raven.prototype.VERSION !== pkg.version) {
      return grunt.util.error(
        'Mismatched version in src/raven.js: ' +
          Raven.prototype.VERSION +
          ' (should be ' +
          pkg.version +
          ')'
      );
    }

    if (grunt.option('dev')) {
      pkg.release = 'dev';
    } else {
      pkg.release = pkg.version;
    }
    grunt.config.set('pkg', pkg);
  });

  grunt.registerTask('config:ci', 'Verify CI config', function() {
    if (!process.env.SAUCE_USERNAME)
      console.warn('No SAUCE_USERNAME env variable defined.');
    if (!process.env.SAUCE_ACCESS_KEY)
      console.warn('No SAUCE_ACCESS_KEY env variable defined.');
    if (!process.env.SAUCE_USERNAME || !process.env.SAUCE_ACCESS_KEY) process.exit(1);
  });

  // Grunt contrib tasks
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-connect');
  grunt.loadNpmTasks('grunt-contrib-copy');

  // 3rd party Grunt tasks
  grunt.loadNpmTasks('grunt-browserify');
  grunt.loadNpmTasks('grunt-release');
  grunt.loadNpmTasks('grunt-s3');
  grunt.loadNpmTasks('grunt-gitinfo');
  grunt.loadNpmTasks('grunt-sri');
  grunt.loadNpmTasks('grunt-saucelabs');

  // Build tasks
  grunt.registerTask('_prep', ['clean', 'gitinfo', 'version']);
  grunt.registerTask(
    'browserify.core',
    ['_prep', 'browserify:core'].concat(browserifyPluginTaskNames)
  );
  grunt.registerTask('browserify.plugins-combined', [
    '_prep',
    'browserify:plugins-combined'
  ]);
  grunt.registerTask('build.test', ['_prep', 'browserify.core', 'browserify:test']);
  grunt.registerTask('build.core', ['browserify.core', 'uglify', 'sri:dist']);
  grunt.registerTask('build.plugins-combined', [
    'browserify.plugins-combined',
    'uglify',
    'sri:dist',
    'sri:build'
  ]);
  grunt.registerTask('build', ['build.plugins-combined']);
  grunt.registerTask('dist', ['build.core', 'copy:dist']);

  grunt.registerTask('test:ci', [
    'config:ci',
    'build.test',
    'connect:ci',
    'saucelabs-mocha'
  ]);

  // Webserver tasks
  grunt.registerTask('run:test', ['connect:test']);
  grunt.registerTask('run:docs', ['connect:docs']);

  grunt.registerTask('publish', ['build.plugins-combined', 's3']);
};
<|endoftext|>


<commit_before>'use strict';

var

  _URL__GRUNT_CONFIG_FILE = './config/grunt.json',
  _URL__NPM_MANIFEST_FILE = './package.json',

  _gruntRegisterTasks = require('grunt-register-tasks'),
  _loadGruntTasks = require('load-grunt-tasks'),
  _timeGrunt = require('time-grunt');

module.exports = function ( $grunt ) {

    var

      _$grunt__file = $grunt.file,
      _$grunt__file__readJSON = _$grunt__file.readJSON,

      _plugins = [
          'grunt-contrib-*',
          'grunt-jsonlint'
        ],

      _config = {
          'cfg': _$grunt__file__readJSON( _URL__GRUNT_CONFIG_FILE ),
          'jsonlint': {
              'config': [
                  '<%= cfg.PATH__CONFIG %>/<%= cfg.GLOB__JSON__RECURSIVE %>',
                  '<%= cfg.PATH__ROOT %>/<%= cfg.FILE__BOWERRC %>'
                ],
              'manifests': '<%= cfg.PATH__ROOT %>/<%= cfg.GLOB__MANIFESTS %>'
            },
          'pkg': _$grunt__file__readJSON( _URL__NPM_MANIFEST_FILE )
        },

      _tasks = {
          'default': [
              'jsonlint'
            ]
        };

    _timeGrunt( $grunt ),

    $grunt.initConfig( _config ),

    _loadGruntTasks(
      $grunt,
      {
        'pattern': _plugins,
        'scope': 'devDependencies'
      }
    ),

    _gruntRegisterTasks(
      $grunt,
      _tasks
    );

  };
<commit_msg>chore(grunt): add `copy` task<commit_after>'use strict';

var

  _URL__GRUNT_CONFIG_FILE = './config/grunt.json',
  _URL__NPM_MANIFEST_FILE = './package.json',

  _gruntRegisterTasks = require('grunt-register-tasks'),
  _loadGruntTasks = require('load-grunt-tasks'),
  _timeGrunt = require('time-grunt');

module.exports = function ( $grunt ) {

    var

      _$grunt__file = $grunt.file,
      _$grunt__file__readJSON = _$grunt__file.readJSON,

      _plugins = [
          'grunt-contrib-*',
          'grunt-jsonlint'
        ],

      _config = {
          'cfg': _$grunt__file__readJSON( _URL__GRUNT_CONFIG_FILE ),
          'copy': {
              'src': {
                  'cwd': '<%= cfg.PATH__SRC %>',
                  'dest': '<%= cfg.PATH__DIST %>',
                  'expand': true,
                  'src': '<%= cfg.FILE__TEMPLATE_MUSTACHE %>'
                }
            },
          'jsonlint': {
              'config': [
                  '<%= cfg.PATH__CONFIG %>/<%= cfg.GLOB__JSON__RECURSIVE %>',
                  '<%= cfg.PATH__ROOT %>/<%= cfg.FILE__BOWERRC %>'
                ],
              'manifests': '<%= cfg.PATH__ROOT %>/<%= cfg.GLOB__MANIFESTS %>'
            },
          'pkg': _$grunt__file__readJSON( _URL__NPM_MANIFEST_FILE )
        },

      _tasks = {
          'default': [
              'jsonlint'
            ]
        };

    _timeGrunt( $grunt ),

    $grunt.initConfig( _config ),

    _loadGruntTasks(
      $grunt,
      {
        'pattern': _plugins,
        'scope': 'devDependencies'
      }
    ),

    _gruntRegisterTasks(
      $grunt,
      _tasks
    );

  };
<|endoftext|>