<commit_before>/* jshint node: true */

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

  var homepageRepo = "../su-homepage";

  var globalConfig = {
    themes: ['homepage', 'wilbur', 'bootstrap'], // valid themes
    repos:  { // repos where theme's files should be deployed
      homepage:  '../su-homepage',
      wilbur:    '../themes-dw/wilbur',
      bootstrap: '../themes-dw/bootstrap'
    },
    theme: 'homepage', // default theme, but may be overridden on command line
    repo:  '../su-homepage' // default repo, but may be overridden on command line
  };

  RegExp.quote = require('regexp-quote');
  var btoa = require('btoa');
  // Project configuration.
  grunt.initConfig({

    // Metadata.
    pkg: grunt.file.readJSON('package.json'),
    banner: '/*!\n' +
              ' * Bootstrap v<%= pkg.version %> by @fat and @mdo\n' +
              ' * Copyright <%= grunt.template.today("yyyy") %> <%= pkg.author %>\n' +
              ' * Licensed under <%= _.pluck(pkg.licenses, "url").join(", ") %>\n' +
              ' *\n' +
              ' * Designed and built with all the love in the world by @mdo and @fat.\n' +
              ' */\n\n',
    jqueryCheck: 'if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery") }\n\n',

    globalConfig: globalConfig,

    // Task configuration.
    clean: {
      dist: ['dist']
    },

    jshint: {
      options: {
        jshintrc: 'js/.jshintrc'
      },
      gruntfile: {
        src: 'Gruntfile.js'
      },
      src: {
        src: ['js/*.js']
      },
      test: {
        src: ['js/tests/unit/*.js']
      }
    },

    concat: {
      options: {
        banner: '<%= banner %><%= jqueryCheck %>',
        stripBanners: false
      },
      bootstrap: {
        src: [
          'js/transition.js',
          //'js/alert.js',
          //'js/button.js',
          'js/carousel.js',
          'js/collapse.js',
          //'js/dropdown.js',
          //'js/modal.js',
          //'js/tooltip.js',
          //'js/popover.js',
          //'js/scrollspy.js',
          //'js/tab.js',
          //'js/affix.js'
        ],
        dest: 'dist/js/<%= pkg.name %>.js'
      }
    },

    uglify: {
      options: {
        banner: '<%= banner %>',
        report: 'min'
      },
      bootstrap: {
        src: ['<%= concat.bootstrap.dest %>'],
        dest: 'dist/js/<%= pkg.name %>.min.js'
      }
    },

    recess: {
      options: {
        compile: true,
        banner: '<%= banner %>'
      },
      bootstrap: {
        src: ['less/bootstrap.less'],
        dest: 'dist/css/<%= pkg.name %>.css'
      },
      min: {
        options: {
          compress: true
        },
        src: ['less/bootstrap.less'],
        dest: 'dist/css/<%= pkg.name %>.min.css'
      },
      theme: {
        src: ['less/theme.less'],
        dest: 'dist/css/<%= pkg.name %>-theme.css'
      },
      theme_min: {
        options: {
          compress: true
        },
        src: ['less/theme.less'],
        dest: 'dist/css/<%= pkg.name %>-theme.min.css'
      }
    },

    less: {
      dev: {
        options: {
            paths: ['themes/<%= globalConfig.theme %>/less', 'less']
          , dumpLineNumbers: "comments"
        },
        files: [
          {
            expand: true,
            flatten: true,
            src: 'themes/<%= globalConfig.theme %>/less/[!_]*.less',
            dest: 'themes/<%= globalConfig.theme %>/dist/css',
            ext: '.dev.css'
          }
        ]
      },
      stage: {
        options: {
            paths: ['themes/<%= globalConfig.theme %>/less', 'less']
        },
        files: [
          {
            expand: true,
            flatten: true,
            src: 'themes/<%= globalConfig.theme %>/less/[!_]*.less',
            dest: 'themes/<%= globalConfig.theme %>/dist/css',
            ext: '.css'
          }
        ]
      },
      prod: {
        options: {
            paths: ['themes/<%= globalConfig.theme %>/less', 'less']
          , yuicompress: true
        },
        files: [
          {
            expand: true,
            flatten: true,
            src: 'themes/<%= globalConfig.theme %>/less/[!_]*.less',
            dest: 'themes/<%= globalConfig.theme %>/dist/css',
            ext: '.min.css'
          }
        ]
      }
    },

    copy: {
      fonts: {
        expand: true,
        src: ["fonts/*"],
        dest: 'dist/'
      },
      ucomm: {
        expand: true,
        flatten: true,
        src: ["ucomm/css/*"],
        dest: homepageRepo+'/assets/css/'
      },
      bootstrapCss: {
        expand: true,
        flatten: true,
        src: ['dist/css/*'],
        dest: homepageRepo+'/assets/bootstrap/css'
      },
      bootstrapJs: {
        expand: true,
        flatten: true,
        src: ['dist/js/*'],
        dest: homepageRepo+'/assets/bootstrap/js'
      },
      themeBefore: { // copy customized bootstrap files to bootstrap's build directory
        expand: true,
        flatten: true,
        cwd: 'themes/<%= globalConfig.theme %>', // look for src files in this directory
        src: 'bootstrap/less/*',
        dest: 'less'
      },
      themeAfter: { // copy generated bootstrap files to theme's dist directories
        expand: true,
        cwd: 'dist',
        src: ['css/{bootstrap,bootstrap.min}.css','js/*.js','fonts/*'],
        dest: 'themes/<%= globalConfig.theme %>/dist'
      },
      themeDeploy: { // copy theme's dist directories to appropriate repo
        expand: true,
        cwd: 'themes/<%= globalConfig.theme %>/dist', // look for src files in this directory
        src: '*/*',
        dest: '<%= globalConfig.repo %>/assets'
      }
    },

    qunit: {
      options: {
        inject: 'js/tests/unit/phantom.js'
      },
      files: ['js/tests/*.html']
    },

    connect: {
      server: {
        options: {
          port: 3000,
          base: '.'
        }
      }
    },

    jekyll: {
      docs: {}
    },

    validation: {
      options: {
        reset: true,
        relaxerror: [
            "Bad value X-UA-Compatible for attribute http-equiv on element meta.",
            "Element img is missing required attribute src."
        ]
      },
      files: {
        src: ["_gh_pages/**/*.html"]
      }
    },

    watch: {
      src: {
        files: '<%= jshint.src.src %>',
        tasks: ['jshint:src', 'qunit']
      },
      test: {
        files: '<%= jshint.test.src %>',
        tasks: ['jshint:test', 'qunit']
      },
      recess: {
        files: 'less/*.less',
        tasks: ['recess']
      }
    },

    sed: {
      versionNumber: {
        pattern: (function () {
          var old = grunt.option('oldver')
          return old ? RegExp.quote(old) : old
        })(),
        replacement: grunt.option('newver'),
        recursive: true
      }
    }
  });

  // These plugins provide necessary tasks.
  grunt.loadNpmTasks('browserstack-runner');
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-connect');
  grunt.loadNpmTasks('grunt-contrib-copy');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-qunit');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-html-validation');
  grunt.loadNpmTasks('grunt-jekyll');
  grunt.loadNpmTasks('grunt-recess');
  grunt.loadNpmTasks('grunt-contrib-less'); // added by UComm
  grunt.loadNpmTasks('grunt-sed');

  // Docs HTML validation task
  grunt.registerTask('validate-html', ['jekyll', 'validation']);

  // Test task.
  var testSubtasks = ['dist-css', 'jshint', 'qunit', 'validate-html'];
  // Only run BrowserStack tests under Travis
  if (process.env.TRAVIS) {
    // Only run BrowserStack tests if this is a mainline commit in twbs/bootstrap, or you have your own BrowserStack key
    if ((process.env.TRAVIS_REPO_SLUG === 'twbs/bootstrap' && process.env.TRAVIS_PULL_REQUEST === 'false') || process.env.TWBS_HAVE_OWN_BROWSERSTACK_KEY) {
      testSubtasks.push('browserstack_runner');
    }
  }
  grunt.registerTask('test', testSubtasks);

  // JS distribution task.
  grunt.registerTask('dist-js', ['concat', 'uglify']);

  // CSS distribution task.
  grunt.registerTask('dist-css', ['recess']);

  // Fonts distribution task.
  grunt.registerTask('dist-fonts', ['copy:fonts']);

  // Full distribution task.
  grunt.registerTask('dist', ['clean', 'dist-css', 'dist-fonts', 'dist-js', 'copy:bootstrapCss', 'copy:bootstrapJs']);

  // Default task.
  grunt.registerTask('default', ['test', 'dist', 'build-customizer']);

  // Version numbering task.
  // grunt change-version-number --oldver=A.B.C --newver=X.Y.Z
  // This can be overzealous, so its changes should always be manually reviewed!
  grunt.registerTask('change-version-number', ['sed']);

  // task for building customizer
  grunt.registerTask('build-customizer', 'Add scripts/less files to customizer.', function () {
    var fs = require('fs')

    function getFiles(type) {
      var files = {}
      fs.readdirSync(type)
        .filter(function (path) {
          return type == 'fonts' ? true : new RegExp('\\.' + type + '$').test(path)
        })
        .forEach(function (path) {
          var fullPath = type + '/' + path
          return files[path] = (type == 'fonts' ? btoa(fs.readFileSync(fullPath)) : fs.readFileSync(fullPath, 'utf8'))
        })
      return 'var __' + type + ' = ' + JSON.stringify(files) + '\n'
    }

    var files = getFiles('js') + getFiles('less') + getFiles('fonts')
    fs.writeFileSync('docs-assets/js/raw-files.js', files)
  });

  // UComm tasks
  grunt.registerTask('dev',     ['less:dev']);
  grunt.registerTask('stage',   ['less:stage']);
  grunt.registerTask('prod',    ['less:prod']);
  grunt.registerTask('rebuild', ['dist-css', 'stage', 'deploy']);

  grunt.registerTask('echo', 'Echo back input', function(theme){
    if (typeof theme != "undefined") {
      globalConfig.theme = theme;
      globalConfig.repo  = globalConfig.repos[theme];
    }
    grunt.log.writeln('Repo is ' + globalConfig.repo);
  });

  // Theme tasks
  // typical usage: grunt theme:homepage build deploy

  grunt.registerTask('theme', "Specify theme to be built", function(theme) {
    grunt.log.writeln(globalConfig.theme);
    if (typeof theme == "undefined") {
      grunt.log.writeln("Error: Must specify a theme, e.g. 'grunt theme:homepage' or 'grunt theme:wilbur'");
      return false;
    }
    if (globalConfig.themes.indexOf(theme) < 0) {
      grunt.log.writeln("Error: Must specify a valid theme. Please specify one of");
      grunt.log.writeln(globalConfig.themes);
      return false;
    }
    globalConfig.theme = theme;
    globalConfig.repo  = globalConfig.repos[theme];
    grunt.log.writeln("Set theme to " + globalConfig.theme);
  });

  grunt.registerTask('build', 'Build customized bootstrap and css for a theme', function() {
    grunt.log.writeln("Generating bootstrap files and css for " + globalConfig.theme);
    grunt.task.run([
      'copy:themeBefore', 'dist-css', 'dist-js', 'dist-fonts', 'copy:themeAfter', // build customized bootstrap
      'less:dev', 'less:stage', 'less:prod' // build theme's css
    ]);
  });

  grunt.registerTask('deploy',  ['copy:themeDeploy']);
};
<commit_msg>only build theme-specific files by default, but provide option to build :bootstrap or :all<commit_after>/* jshint node: true */

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

  var homepageRepo = "../su-homepage";

  var globalConfig = {
    themes: ['homepage', 'wilbur', 'bootstrap'], // valid themes
    repos:  { // repos where theme's files should be deployed
      homepage:  '../su-homepage',
      wilbur:    '../themes-dw/wilbur',
      bootstrap: '../themes-dw/bootstrap'
    },
    theme: 'homepage', // default theme, but may be overridden on command line
    repo:  '../su-homepage' // default repo, but may be overridden on command line
  };

  RegExp.quote = require('regexp-quote');
  var btoa = require('btoa');
  // Project configuration.
  grunt.initConfig({

    // Metadata.
    pkg: grunt.file.readJSON('package.json'),
    banner: '/*!\n' +
              ' * Bootstrap v<%= pkg.version %> by @fat and @mdo\n' +
              ' * Copyright <%= grunt.template.today("yyyy") %> <%= pkg.author %>\n' +
              ' * Licensed under <%= _.pluck(pkg.licenses, "url").join(", ") %>\n' +
              ' *\n' +
              ' * Designed and built with all the love in the world by @mdo and @fat.\n' +
              ' */\n\n',
    jqueryCheck: 'if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery") }\n\n',

    globalConfig: globalConfig,

    // Task configuration.
    clean: {
      dist: ['dist']
    },

    jshint: {
      options: {
        jshintrc: 'js/.jshintrc'
      },
      gruntfile: {
        src: 'Gruntfile.js'
      },
      src: {
        src: ['js/*.js']
      },
      test: {
        src: ['js/tests/unit/*.js']
      }
    },

    concat: {
      options: {
        banner: '<%= banner %><%= jqueryCheck %>',
        stripBanners: false
      },
      bootstrap: {
        src: [
          'js/transition.js',
          //'js/alert.js',
          //'js/button.js',
          'js/carousel.js',
          'js/collapse.js',
          //'js/dropdown.js',
          //'js/modal.js',
          //'js/tooltip.js',
          //'js/popover.js',
          //'js/scrollspy.js',
          //'js/tab.js',
          //'js/affix.js'
        ],
        dest: 'dist/js/<%= pkg.name %>.js'
      }
    },

    uglify: {
      options: {
        banner: '<%= banner %>',
        report: 'min'
      },
      bootstrap: {
        src: ['<%= concat.bootstrap.dest %>'],
        dest: 'dist/js/<%= pkg.name %>.min.js'
      }
    },

    recess: {
      options: {
        compile: true,
        banner: '<%= banner %>'
      },
      bootstrap: {
        src: ['less/bootstrap.less'],
        dest: 'dist/css/<%= pkg.name %>.css'
      },
      min: {
        options: {
          compress: true
        },
        src: ['less/bootstrap.less'],
        dest: 'dist/css/<%= pkg.name %>.min.css'
      },
      theme: {
        src: ['less/theme.less'],
        dest: 'dist/css/<%= pkg.name %>-theme.css'
      },
      theme_min: {
        options: {
          compress: true
        },
        src: ['less/theme.less'],
        dest: 'dist/css/<%= pkg.name %>-theme.min.css'
      }
    },

    less: {
      dev: {
        options: {
            paths: ['themes/<%= globalConfig.theme %>/less', 'less']
          , dumpLineNumbers: "comments"
        },
        files: [
          {
            expand: true,
            flatten: true,
            src: 'themes/<%= globalConfig.theme %>/less/[!_]*.less',
            dest: 'themes/<%= globalConfig.theme %>/dist/css',
            ext: '.dev.css'
          }
        ]
      },
      stage: {
        options: {
            paths: ['themes/<%= globalConfig.theme %>/less', 'less']
        },
        files: [
          {
            expand: true,
            flatten: true,
            src: 'themes/<%= globalConfig.theme %>/less/[!_]*.less',
            dest: 'themes/<%= globalConfig.theme %>/dist/css',
            ext: '.css'
          }
        ]
      },
      prod: {
        options: {
            paths: ['themes/<%= globalConfig.theme %>/less', 'less']
          , yuicompress: true
        },
        files: [
          {
            expand: true,
            flatten: true,
            src: 'themes/<%= globalConfig.theme %>/less/[!_]*.less',
            dest: 'themes/<%= globalConfig.theme %>/dist/css',
            ext: '.min.css'
          }
        ]
      }
    },

    copy: {
      fonts: {
        expand: true,
        src: ["fonts/*"],
        dest: 'dist/'
      },
      ucomm: {
        expand: true,
        flatten: true,
        src: ["ucomm/css/*"],
        dest: homepageRepo+'/assets/css/'
      },
      bootstrapCss: {
        expand: true,
        flatten: true,
        src: ['dist/css/*'],
        dest: homepageRepo+'/assets/bootstrap/css'
      },
      bootstrapJs: {
        expand: true,
        flatten: true,
        src: ['dist/js/*'],
        dest: homepageRepo+'/assets/bootstrap/js'
      },
      themeBefore: { // copy customized bootstrap files to bootstrap's build directory
        expand: true,
        flatten: true,
        cwd: 'themes/<%= globalConfig.theme %>', // look for src files in this directory
        src: 'bootstrap/less/*',
        dest: 'less'
      },
      themeAfter: { // copy generated bootstrap files to theme's dist directories
        expand: true,
        cwd: 'dist',
        src: ['css/{bootstrap,bootstrap.min}.css','js/*.js','fonts/*'],
        dest: 'themes/<%= globalConfig.theme %>/dist'
      },
      themeDeploy: { // copy theme's dist directories to appropriate repo
        expand: true,
        cwd: 'themes/<%= globalConfig.theme %>/dist', // look for src files in this directory
        src: '*/*',
        dest: '<%= globalConfig.repo %>/assets'
      }
    },

    qunit: {
      options: {
        inject: 'js/tests/unit/phantom.js'
      },
      files: ['js/tests/*.html']
    },

    connect: {
      server: {
        options: {
          port: 3000,
          base: '.'
        }
      }
    },

    jekyll: {
      docs: {}
    },

    validation: {
      options: {
        reset: true,
        relaxerror: [
            "Bad value X-UA-Compatible for attribute http-equiv on element meta.",
            "Element img is missing required attribute src."
        ]
      },
      files: {
        src: ["_gh_pages/**/*.html"]
      }
    },

    watch: {
      src: {
        files: '<%= jshint.src.src %>',
        tasks: ['jshint:src', 'qunit']
      },
      test: {
        files: '<%= jshint.test.src %>',
        tasks: ['jshint:test', 'qunit']
      },
      recess: {
        files: 'less/*.less',
        tasks: ['recess']
      }
    },

    sed: {
      versionNumber: {
        pattern: (function () {
          var old = grunt.option('oldver')
          return old ? RegExp.quote(old) : old
        })(),
        replacement: grunt.option('newver'),
        recursive: true
      }
    }
  });

  // These plugins provide necessary tasks.
  grunt.loadNpmTasks('browserstack-runner');
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-connect');
  grunt.loadNpmTasks('grunt-contrib-copy');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-qunit');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-html-validation');
  grunt.loadNpmTasks('grunt-jekyll');
  grunt.loadNpmTasks('grunt-recess');
  grunt.loadNpmTasks('grunt-contrib-less'); // added by UComm
  grunt.loadNpmTasks('grunt-sed');

  // Docs HTML validation task
  grunt.registerTask('validate-html', ['jekyll', 'validation']);

  // Test task.
  var testSubtasks = ['dist-css', 'jshint', 'qunit', 'validate-html'];
  // Only run BrowserStack tests under Travis
  if (process.env.TRAVIS) {
    // Only run BrowserStack tests if this is a mainline commit in twbs/bootstrap, or you have your own BrowserStack key
    if ((process.env.TRAVIS_REPO_SLUG === 'twbs/bootstrap' && process.env.TRAVIS_PULL_REQUEST === 'false') || process.env.TWBS_HAVE_OWN_BROWSERSTACK_KEY) {
      testSubtasks.push('browserstack_runner');
    }
  }
  grunt.registerTask('test', testSubtasks);

  // JS distribution task.
  grunt.registerTask('dist-js', ['concat', 'uglify']);

  // CSS distribution task.
  grunt.registerTask('dist-css', ['recess']);

  // Fonts distribution task.
  grunt.registerTask('dist-fonts', ['copy:fonts']);

  // Full distribution task.
  grunt.registerTask('dist', ['clean', 'dist-css', 'dist-fonts', 'dist-js', 'copy:bootstrapCss', 'copy:bootstrapJs']);

  // Default task.
  grunt.registerTask('default', ['test', 'dist', 'build-customizer']);

  // Version numbering task.
  // grunt change-version-number --oldver=A.B.C --newver=X.Y.Z
  // This can be overzealous, so its changes should always be manually reviewed!
  grunt.registerTask('change-version-number', ['sed']);

  // task for building customizer
  grunt.registerTask('build-customizer', 'Add scripts/less files to customizer.', function () {
    var fs = require('fs')

    function getFiles(type) {
      var files = {}
      fs.readdirSync(type)
        .filter(function (path) {
          return type == 'fonts' ? true : new RegExp('\\.' + type + '$').test(path)
        })
        .forEach(function (path) {
          var fullPath = type + '/' + path
          return files[path] = (type == 'fonts' ? btoa(fs.readFileSync(fullPath)) : fs.readFileSync(fullPath, 'utf8'))
        })
      return 'var __' + type + ' = ' + JSON.stringify(files) + '\n'
    }

    var files = getFiles('js') + getFiles('less') + getFiles('fonts')
    fs.writeFileSync('docs-assets/js/raw-files.js', files)
  });

  // UComm tasks
  grunt.registerTask('dev',     ['less:dev']);
  grunt.registerTask('stage',   ['less:stage']);
  grunt.registerTask('prod',    ['less:prod']);
  grunt.registerTask('rebuild', ['dist-css', 'stage', 'deploy']);

  grunt.registerTask('echo', 'Echo back input', function(theme){
    if (typeof theme != "undefined") {
      globalConfig.theme = theme;
      globalConfig.repo  = globalConfig.repos[theme];
    }
    grunt.log.writeln('Repo is ' + globalConfig.repo);
  });

  // Theme tasks
  // typical usage: grunt theme:homepage build deploy

  grunt.registerTask('theme', "Specify theme to be built", function(theme) {
    grunt.log.writeln(globalConfig.theme);
    if (typeof theme == "undefined") {
      grunt.log.writeln("Error: Must specify a theme, e.g. 'grunt theme:homepage' or 'grunt theme:wilbur'");
      return false;
    }
    if (globalConfig.themes.indexOf(theme) < 0) {
      grunt.log.writeln("Error: Must specify a valid theme. Please specify one of");
      grunt.log.writeln(globalConfig.themes);
      return false;
    }
    globalConfig.theme = theme;
    globalConfig.repo  = globalConfig.repos[theme];
    grunt.log.writeln("Set theme to " + globalConfig.theme);
  });

  // grunt build, grunt build:theme (same as grunt build), grunt build:bootstrap or grunt build:all
  grunt.registerTask('build', 'Build customized bootstrap and css for a theme', function(target) {
    if (typeof target == "undefined") { // if no target specified, just build theme files
      target = 'theme';
    }
    if (target == 'bootstrap' || target == 'all') {
      grunt.log.writeln("Generating bootstrap files for " + globalConfig.theme);
      grunt.task.run([
        'copy:themeBefore', 'dist-css', 'dist-js', 'dist-fonts', 'copy:themeAfter'
      ]);
    }
    if (target == 'theme' || target == 'all') {
      grunt.log.writeln("Generating theme files for " + globalConfig.theme);
      grunt.task.run([
        'less:dev', 'less:stage', 'less:prod' // build theme's css
      ]);
    }
  });

  grunt.registerTask('deploy',  ['copy:themeDeploy']);
};
<|endoftext|>


<commit_before>import _ from 'lodash'
import fs from 'fs'
import path from 'path'
import webpack from 'webpack'
import Config from 'webpack-configurator'
import ExtractTextPlugin from 'extract-text-webpack-plugin'
import StaticSiteGeneratorPlugin from 'static-site-generator-webpack-plugin'
import { StatsWriterPlugin } from 'webpack-stats-plugin'
import HardSourceWebpackPlugin from 'hard-source-webpack-plugin'

import webpackModifyValidate from './webpack-modify-validate'

const debug = require(`debug`)(`gatsby:webpack-config`)
const WebpackMD5Hash = require(`webpack-md5-hash`)
const OfflinePlugin = require(`offline-plugin`)
const ChunkManifestPlugin = require(`chunk-manifest-webpack-plugin`)
const { pagesDB, siteDB } = require(`../utils/globals`)
const { layoutComponentChunkName } = require(`./js-chunk-names`)
const babelConfig = require(`./babel-config`)

// Five stages or modes:
//   1) develop: for `gatsby develop` command, hot reload and CSS injection into page
//   2) develop-html: same as develop without react-hmre in the babel config for html renderer
//   3) build-css: build styles.css file
//   4) build-html: build all HTML files
//   5) build-javascript: Build js chunks for Single Page App in production

module.exports = (program, directory, suppliedStage, webpackPort = 1500, pages = []) => {
  const babelStage = suppliedStage
  const stage = (suppliedStage === `develop-html`) ? `develop` : suppliedStage

  debug(`Loading webpack config for stage "${stage}"`)
  function output () {
    switch (stage) {
      case `develop`:
        return {
          path: directory,
          filename: `[name].js`,
          publicPath: `http://${program.host}:${webpackPort}/`,
        }
      case `build-css`:
        // Webpack will always generate a resultant javascript file.
        // But we don't want it for this step. Deleted by build-css.js.
        return {
          path: `${directory}/public`,
          filename: `bundle-for-css.js`,
          publicPath: program.prefixLinks ? `${siteDB().get(`config`).linkPrefix}/` : `/`,
        }
      case `build-html`:
        // A temp file required by static-site-generator-plugin. See plugins() below.
        // Deleted by build-html.js, since it's not needed for production.
        return {
          path: `${directory}/public`,
          filename: `render-page.js`,
          libraryTarget: `umd`,
          publicPath: program.prefixLinks ? `${siteDB().get(`config`).linkPrefix}/` : `/`,
        }
      case `build-javascript`:
        return {
          //filename: '[name].js',
          filename: `[name]-[chunkhash].js`,
          chunkFilename: `[name]-[chunkhash].js`,
          path: `${directory}/public`,
          publicPath: program.prefixLinks ? `${siteDB().get(`config`).linkPrefix}/` : `/`,
        }
      default:
        throw new Error(`The state requested ${stage} doesn't exist.`)
    }
  }

  function entry () {
    switch (stage) {
      case `develop`:
        return {
          commons: [
            `${require.resolve('webpack-dev-server/client')}?http://${program.host}:${webpackPort}/`,
            require.resolve(`webpack/hot/only-dev-server`),
            require.resolve(`react-hot-loader/patch`),
            `${directory}/.intermediate-representation/app`,
          ],
        }
      case `build-css`:
        return {
          main: `${directory}/.intermediate-representation/app`,
        }
      case `build-html`:
        return {
          main: `${__dirname}/static-entry`,
        }
      case `build-javascript`:
        return {
          app: `${directory}/.intermediate-representation/production-app`,
        }
      default:
        throw new Error(`The state requested ${stage} doesn't exist.`)
    }
  }

  function plugins () {
    switch (stage) {
      case `develop`:
        return [
          new webpack.optimize.OccurenceOrderPlugin(),
          new webpack.HotModuleReplacementPlugin(),
          new webpack.NoErrorsPlugin(),
          new webpack.DefinePlugin({
            'process.env': {
              NODE_ENV: JSON.stringify(process.env.NODE_ENV ? process.env.NODE_ENV : `development`),
            },
            __PREFIX_LINKS__: program.prefixLinks,
            __LINK_PREFIX__: JSON.stringify(siteDB().get(`config`).linkPrefix),
          }),
          new HardSourceWebpackPlugin({
            cacheDirectory: `${process.cwd()}/.cache/[confighash]`,
            recordsPath: `${process.cwd()}/.cache/[confighash]/records.json`,
            configHash: (webpackConfig) => stage,
            environmentPaths: {
              root: process.cwd(),
              directories: ['node_modules'],
              files: ['package.json'],
            },
          }),
        ]
      case `build-css`:
        return [
          new webpack.DefinePlugin({
            'process.env': {
              NODE_ENV: JSON.stringify(process.env.NODE_ENV ? process.env.NODE_ENV : `production`),
            },
            __PREFIX_LINKS__: program.prefixLinks,
            __LINK_PREFIX__: JSON.stringify(siteDB().get(`config`).linkPrefix),
          }),
          new ExtractTextPlugin(`styles.css`, { allChunks: true }),
          new HardSourceWebpackPlugin({
            cacheDirectory: `${process.cwd()}/.cache/[confighash]`,
            recordsPath: `${process.cwd()}/.cache/[confighash]/records.json`,
            configHash: (webpackConfig) => stage,
            environmentPaths: {
              root: process.cwd(),
              directories: ['node_modules'],
              files: ['package.json'],
            },
          }),
        ]
      case `build-html`:
        return [
          new StaticSiteGeneratorPlugin(`render-page.js`, pages),
          new webpack.DefinePlugin({
            'process.env': {
              NODE_ENV: JSON.stringify(process.env.NODE_ENV ? process.env.NODE_ENV : `production`),
            },
            __PREFIX_LINKS__: program.prefixLinks,
            __LINK_PREFIX__: JSON.stringify(siteDB().get(`config`).linkPrefix),
          }),
          new ExtractTextPlugin(`build-html-styles.css`),
          new HardSourceWebpackPlugin({
            cacheDirectory: `${process.cwd()}/.cache/[confighash]`,
            recordsPath: `${process.cwd()}/.cache/[confighash]/records.json`,
            configHash: (webpackConfig) => stage,
            environmentPaths: {
              root: process.cwd(),
              directories: ['node_modules'],
              files: ['package.json'],
            },
          }),
        ]
      case `build-javascript`: {
        // Get array of page template component names.
        let components = Array.from(pagesDB().values()).map(page => page.component)
        components = components.map(component => layoutComponentChunkName(program.directory, component))
        components = _.uniq(components)
        return [
          // Moment.js includes 100s of KBs of extra localization data
          // by default in Webpack that most sites don't want.
          // This line disables that.
          // TODO remove this now that loading moment.js isn't common w/ new
          // graphql data layer?
          new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
          new WebpackMD5Hash(),
          new webpack.optimize.DedupePlugin(),
          // Extract "commons" chunk from the app entry and all
          // page components.
          new webpack.optimize.CommonsChunkPlugin({
            name: `commons`,
            chunks: [
              `app`,
              ...components,
            ],
            // The more page components there are, the higher we raise the bar
            // for merging in page-specific JS libs into the commons chunk. The
            // two principles here is a) keep the TTI (time to interaction) as
            // low as possible so that means keeping commons.js small with
            // critical code (e.g. React) and b) is we want to push JS
            // parse/eval work as close as possible to when it's used.  Since
            // most people don't navigate to most pages, take tradeoff of
            // loading/evaling modules multiple times over loading/evaling lots
            // of unused code on the initial opening of the app.
            //
            // Use Math.max as there must be at least two chunks.
            minChunks: Math.max(2, Math.floor(components.length / 2)),
          }),
          // Add a few global variables. Set NODE_ENV to production (enables
          // optimizations for React) and whether prefixing links is enabled
          // (__PREFIX_LINKS__) and what the link prefix is (__LINK_PREFIX__).
          new webpack.DefinePlugin({
            'process.env': {
              NODE_ENV: JSON.stringify(process.env.NODE_ENV ? process.env.NODE_ENV : `production`),
            },
            __PREFIX_LINKS__: program.prefixLinks,
            __LINK_PREFIX__: JSON.stringify(siteDB().get(`config`).linkPrefix),
          }),
          // Extract CSS so it doesn't get added to JS bundles.
          new ExtractTextPlugin(`build-js-styles.css`),
          // Write out mapping between chunk names and their hashed names. We use
          // this to add the needed javascript files to each HTML page.
          new StatsWriterPlugin(),
          // Extract the webpack chunk manifest out of commons.js so commons.js
          // doesn't get changed everytime you build. This increases the cache-hit
          // rate for commons.js.
          //new ChunkManifestPlugin({
            //filename: "chunk-manifest.json",
            //manifestVariable: "webpackManifest"
          //}),
          // Minify Javascript.
          new webpack.optimize.UglifyJsPlugin({
            compress: {
              screw_ie8: true, // React doesn't support IE8
              warnings: false
            },
            mangle: {
              screw_ie8: true
            },
            output: {
              comments: false,
              screw_ie8: true
            }
          }),
          // Ensure module order stays the same. Supposibly fixed in webpack 2.0.
          new webpack.optimize.OccurenceOrderPlugin(),
          // Enable the offline plugin to add a service worker.
          new OfflinePlugin({
            AppCache: false,
            publicPath: program.prefixLinks ? `${siteDB().get(`config`).linkPrefix}/` : `/`,
            relativePaths: false,
            ServiceWorker: {
              events: true,
            },
          }),
          new HardSourceWebpackPlugin({
            cacheDirectory: `${process.cwd()}/.cache/[confighash]`,
            recordsPath: `${process.cwd()}/.cache/[confighash]/records.json`,
            configHash: (webpackConfig) => stage,
            environmentPaths: {
              root: process.cwd(),
              directories: ['node_modules'],
              files: ['package.json'],
            },
          }),
        ]
      }
      default:
        throw new Error(`The state requested ${stage} doesn't exist.`)
    }
  }

  function resolve () {
    return {
      extensions: [
        ``,
        `.js`,
        `.jsx`,
        `.cjsx`,
        `.coffee`,
      ],
      // Hierarchy of directories for Webpack to look for module.
      // First is the site directory.
      // Then in the special directory of isomorphic modules Gatsby ships with.
      // Then the site's node_modules directory
      // and last the Gatsby node_modules directory.
      root: [
        directory,
        path.resolve(__dirname, `..`, `isomorphic`),
      ],
      modulesDirectories: [
        `${directory}/node_modules`,
        `node_modules`,
      ],
    }
  }

  function devtool () {
    switch (stage) {
      case `develop`:
        return `eval`
      case `build-html`:
        return false
      case `build-javascript`:
        return `source-map`
      default:
        return false
    }
  }

  function module (config) {
    // Common config for every env.
    config.loader(`cjsx`, {
      test: /\.cjsx$/,
      loaders: [`coffee`, `cjsx`],
    })
    config.loader(`js`, {
      test: /\.jsx?$/, // Accept either .js or .jsx files.
      exclude: /(node_modules|bower_components)/,
      loader: `babel`,
      query: babelConfig(program, babelStage),
    })
    config.loader(`coffee`, {
      test: /\.coffee$/,
      loader: `coffee`,
    })
    config.loader(`json`, {
      test: /\.json$/,
      loaders: [`json`],
    })
    // "file" loader makes sure those assets end up in the `public` folder.
    // When you `import` an asset, you get its filename.
    config.loader(`file-loader`, {
      test: /\.(ico|eot|otf|webp|ttf)(\?.*)?$/,
      loader: 'file',
      query: {
        name: 'static/[name].[hash:8].[ext]'
      }
    })
    // "url" loader works just like "file" loader but it also embeds
    // assets smaller than specified size as data URLs to avoid requests.
    config.loader(`url-loader`, {
      test: /\.(svg|jpg|jpeg|png|gif|mp4|webm|wav|mp3|m4a|aac|oga)(\?.*)?$/,
      loader: 'url',
      query: {
        limit: 7500,
        name: 'static/[name].[hash:8].[ext]'
      }
    })
    // Font loader.
    config.loader(`woff`, {
      test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
      loader: `url`,
      query: {
        limit: 15000, // Set high limit for inlining fonts as they're in the
        // critical path for rendering.
        name: `static/[name].[hash:8].[ext]`,
        mimetype: `application/font-woff`,
      }
    })

    const cssModulesConf = `css?modules&minimize&importLoaders=1`
    const cssModulesConfDev =
      `${cssModulesConf}&sourceMap&localIdentName=[name]---[local]---[hash:base64:5]`

    switch (stage) {
      case `develop`:

        config.loader(`css`, {
          test: /\.css$/,
          exclude: /\.module\.css$/,
          loaders: [`style`, `css`, `postcss`],
        })

        // CSS modules
        config.loader(`cssModules`, {
          test: /\.module\.css$/,
          loaders: [`style`, cssModulesConfDev, `postcss`],
        })

        config.merge({
          postcss (wp) {
            return [
              require(`postcss-import`)({ addDependencyTo: wp }),
              require(`postcss-cssnext`)({ browsers: `last 2 versions` }),
              require(`postcss-browser-reporter`),
              require(`postcss-reporter`),
            ]
          },
        })
        return config

      case `build-css`:
        config.loader(`css`, {
          test: /\.css$/,
          loader: ExtractTextPlugin.extract([`css?minimize`, `postcss`]),
          exclude: /\.module\.css$/,
        })

        // CSS modules
        config.loader(`cssModules`, {
          test: /\.module\.css$/,
          loader: ExtractTextPlugin.extract(`style`, [cssModulesConf, `postcss`]),
        })
        config.merge({
          postcss: [
            require(`postcss-import`)(),
            require(`postcss-cssnext`)({
              browsers: `last 2 versions`,
            }),
          ],
        })
        return config

      case `build-html`:
        // We don't deal with CSS at all when building the HTML.
        // The 'null' loader is used to prevent 'module not found' errors.
        // On the other hand CSS modules loaders are necessary.

        config.loader(`css`, {
          test: /\.css$/,
          exclude: /\.module\.css$/,
          loader: `null`,
        })

        // CSS modules
        config.loader(`cssModules`, {
          test: /\.module\.css$/,
          loader: ExtractTextPlugin.extract(`style`, [cssModulesConf, `postcss`]),
        })

        return config

      case `build-javascript`:
        // we don't deal with css at all when building the javascript.  but
        // still need to process the css so offline-plugin knows about the
        // various assets referenced in your css.
        //
        // It's also necessary to process CSS Modules so your JS knows the
        // classNames to use.

        config.loader(`css`, {
          test: /\.css$/,
          exclude: /\.module\.css$/,
          //loader: `null`,
          loader: ExtractTextPlugin.extract([`css`]),
        })

        // CSS modules
        config.loader(`cssModules`, {
          test: /\.module\.css$/,
          loader: ExtractTextPlugin.extract(`style`, [cssModulesConf, `postcss`]),
        })

        return config

      default:
        return config
    }
  }

  function resolveLoader () {
    const root = [
      path.resolve(directory, `node_modules`),
      path.resolve(directory, `node_modules/gatsby/node_modules`),
    ]

    const userLoaderDirectoryPath = path.resolve(directory, 'loaders')

    try {
      if (fs.statSync(userLoaderDirectoryPath).isDirectory()) {
        root.push(userLoaderDirectoryPath)
      }
    } catch (e) {
      if (e && e.code !== `ENOENT`) {
        console.log(e)
      }
    }

    return {
      root,
      modulesDirectories: [
        `node_modules`,
      ],
    }
  }

  const config = new Config()

  config.merge({
    // Context is the base directory for resolving the entry option.
    context: `${directory}`,
    node: {
      __filename: true,
    },
    entry: entry(),
    debug: true,
    // Certain "isomorphic" packages have different entry points for browser and server
    // (see https://github.com/defunctzombie/package-browser-field-spec);
    // setting the target tells webpack which file to include, ie. browser vs main.
    target: stage === `build-html` ? `node` : `web`,
    profile: stage === `production`,
    devtool: devtool(),
    output: output(),
    resolveLoader: resolveLoader(),
    plugins: plugins(),
    resolve: resolve(),
  })

  return webpackModifyValidate(module, config, stage)
}
<commit_msg>Remove another place where added gatsby/node_modules to webpack config<commit_after>import _ from 'lodash'
import fs from 'fs'
import path from 'path'
import webpack from 'webpack'
import Config from 'webpack-configurator'
import ExtractTextPlugin from 'extract-text-webpack-plugin'
import StaticSiteGeneratorPlugin from 'static-site-generator-webpack-plugin'
import { StatsWriterPlugin } from 'webpack-stats-plugin'
import HardSourceWebpackPlugin from 'hard-source-webpack-plugin'

import webpackModifyValidate from './webpack-modify-validate'

const debug = require(`debug`)(`gatsby:webpack-config`)
const WebpackMD5Hash = require(`webpack-md5-hash`)
const OfflinePlugin = require(`offline-plugin`)
const ChunkManifestPlugin = require(`chunk-manifest-webpack-plugin`)
const { pagesDB, siteDB } = require(`../utils/globals`)
const { layoutComponentChunkName } = require(`./js-chunk-names`)
const babelConfig = require(`./babel-config`)

// Five stages or modes:
//   1) develop: for `gatsby develop` command, hot reload and CSS injection into page
//   2) develop-html: same as develop without react-hmre in the babel config for html renderer
//   3) build-css: build styles.css file
//   4) build-html: build all HTML files
//   5) build-javascript: Build js chunks for Single Page App in production

module.exports = (program, directory, suppliedStage, webpackPort = 1500, pages = []) => {
  const babelStage = suppliedStage
  const stage = (suppliedStage === `develop-html`) ? `develop` : suppliedStage

  debug(`Loading webpack config for stage "${stage}"`)
  function output () {
    switch (stage) {
      case `develop`:
        return {
          path: directory,
          filename: `[name].js`,
          publicPath: `http://${program.host}:${webpackPort}/`,
        }
      case `build-css`:
        // Webpack will always generate a resultant javascript file.
        // But we don't want it for this step. Deleted by build-css.js.
        return {
          path: `${directory}/public`,
          filename: `bundle-for-css.js`,
          publicPath: program.prefixLinks ? `${siteDB().get(`config`).linkPrefix}/` : `/`,
        }
      case `build-html`:
        // A temp file required by static-site-generator-plugin. See plugins() below.
        // Deleted by build-html.js, since it's not needed for production.
        return {
          path: `${directory}/public`,
          filename: `render-page.js`,
          libraryTarget: `umd`,
          publicPath: program.prefixLinks ? `${siteDB().get(`config`).linkPrefix}/` : `/`,
        }
      case `build-javascript`:
        return {
          //filename: '[name].js',
          filename: `[name]-[chunkhash].js`,
          chunkFilename: `[name]-[chunkhash].js`,
          path: `${directory}/public`,
          publicPath: program.prefixLinks ? `${siteDB().get(`config`).linkPrefix}/` : `/`,
        }
      default:
        throw new Error(`The state requested ${stage} doesn't exist.`)
    }
  }

  function entry () {
    switch (stage) {
      case `develop`:
        return {
          commons: [
            `${require.resolve('webpack-dev-server/client')}?http://${program.host}:${webpackPort}/`,
            require.resolve(`webpack/hot/only-dev-server`),
            require.resolve(`react-hot-loader/patch`),
            `${directory}/.intermediate-representation/app`,
          ],
        }
      case `build-css`:
        return {
          main: `${directory}/.intermediate-representation/app`,
        }
      case `build-html`:
        return {
          main: `${__dirname}/static-entry`,
        }
      case `build-javascript`:
        return {
          app: `${directory}/.intermediate-representation/production-app`,
        }
      default:
        throw new Error(`The state requested ${stage} doesn't exist.`)
    }
  }

  function plugins () {
    switch (stage) {
      case `develop`:
        return [
          new webpack.optimize.OccurenceOrderPlugin(),
          new webpack.HotModuleReplacementPlugin(),
          new webpack.NoErrorsPlugin(),
          new webpack.DefinePlugin({
            'process.env': {
              NODE_ENV: JSON.stringify(process.env.NODE_ENV ? process.env.NODE_ENV : `development`),
            },
            __PREFIX_LINKS__: program.prefixLinks,
            __LINK_PREFIX__: JSON.stringify(siteDB().get(`config`).linkPrefix),
          }),
          new HardSourceWebpackPlugin({
            cacheDirectory: `${process.cwd()}/.cache/[confighash]`,
            recordsPath: `${process.cwd()}/.cache/[confighash]/records.json`,
            configHash: (webpackConfig) => stage,
            environmentPaths: {
              root: process.cwd(),
              directories: ['node_modules'],
              files: ['package.json'],
            },
          }),
        ]
      case `build-css`:
        return [
          new webpack.DefinePlugin({
            'process.env': {
              NODE_ENV: JSON.stringify(process.env.NODE_ENV ? process.env.NODE_ENV : `production`),
            },
            __PREFIX_LINKS__: program.prefixLinks,
            __LINK_PREFIX__: JSON.stringify(siteDB().get(`config`).linkPrefix),
          }),
          new ExtractTextPlugin(`styles.css`, { allChunks: true }),
          new HardSourceWebpackPlugin({
            cacheDirectory: `${process.cwd()}/.cache/[confighash]`,
            recordsPath: `${process.cwd()}/.cache/[confighash]/records.json`,
            configHash: (webpackConfig) => stage,
            environmentPaths: {
              root: process.cwd(),
              directories: ['node_modules'],
              files: ['package.json'],
            },
          }),
        ]
      case `build-html`:
        return [
          new StaticSiteGeneratorPlugin(`render-page.js`, pages),
          new webpack.DefinePlugin({
            'process.env': {
              NODE_ENV: JSON.stringify(process.env.NODE_ENV ? process.env.NODE_ENV : `production`),
            },
            __PREFIX_LINKS__: program.prefixLinks,
            __LINK_PREFIX__: JSON.stringify(siteDB().get(`config`).linkPrefix),
          }),
          new ExtractTextPlugin(`build-html-styles.css`),
          new HardSourceWebpackPlugin({
            cacheDirectory: `${process.cwd()}/.cache/[confighash]`,
            recordsPath: `${process.cwd()}/.cache/[confighash]/records.json`,
            configHash: (webpackConfig) => stage,
            environmentPaths: {
              root: process.cwd(),
              directories: ['node_modules'],
              files: ['package.json'],
            },
          }),
        ]
      case `build-javascript`: {
        // Get array of page template component names.
        let components = Array.from(pagesDB().values()).map(page => page.component)
        components = components.map(component => layoutComponentChunkName(program.directory, component))
        components = _.uniq(components)
        return [
          // Moment.js includes 100s of KBs of extra localization data
          // by default in Webpack that most sites don't want.
          // This line disables that.
          // TODO remove this now that loading moment.js isn't common w/ new
          // graphql data layer?
          new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
          new WebpackMD5Hash(),
          new webpack.optimize.DedupePlugin(),
          // Extract "commons" chunk from the app entry and all
          // page components.
          new webpack.optimize.CommonsChunkPlugin({
            name: `commons`,
            chunks: [
              `app`,
              ...components,
            ],
            // The more page components there are, the higher we raise the bar
            // for merging in page-specific JS libs into the commons chunk. The
            // two principles here is a) keep the TTI (time to interaction) as
            // low as possible so that means keeping commons.js small with
            // critical code (e.g. React) and b) is we want to push JS
            // parse/eval work as close as possible to when it's used.  Since
            // most people don't navigate to most pages, take tradeoff of
            // loading/evaling modules multiple times over loading/evaling lots
            // of unused code on the initial opening of the app.
            //
            // Use Math.max as there must be at least two chunks.
            minChunks: Math.max(2, Math.floor(components.length / 2)),
          }),
          // Add a few global variables. Set NODE_ENV to production (enables
          // optimizations for React) and whether prefixing links is enabled
          // (__PREFIX_LINKS__) and what the link prefix is (__LINK_PREFIX__).
          new webpack.DefinePlugin({
            'process.env': {
              NODE_ENV: JSON.stringify(process.env.NODE_ENV ? process.env.NODE_ENV : `production`),
            },
            __PREFIX_LINKS__: program.prefixLinks,
            __LINK_PREFIX__: JSON.stringify(siteDB().get(`config`).linkPrefix),
          }),
          // Extract CSS so it doesn't get added to JS bundles.
          new ExtractTextPlugin(`build-js-styles.css`),
          // Write out mapping between chunk names and their hashed names. We use
          // this to add the needed javascript files to each HTML page.
          new StatsWriterPlugin(),
          // Extract the webpack chunk manifest out of commons.js so commons.js
          // doesn't get changed everytime you build. This increases the cache-hit
          // rate for commons.js.
          //new ChunkManifestPlugin({
            //filename: "chunk-manifest.json",
            //manifestVariable: "webpackManifest"
          //}),
          // Minify Javascript.
          new webpack.optimize.UglifyJsPlugin({
            compress: {
              screw_ie8: true, // React doesn't support IE8
              warnings: false
            },
            mangle: {
              screw_ie8: true
            },
            output: {
              comments: false,
              screw_ie8: true
            }
          }),
          // Ensure module order stays the same. Supposibly fixed in webpack 2.0.
          new webpack.optimize.OccurenceOrderPlugin(),
          // Enable the offline plugin to add a service worker.
          new OfflinePlugin({
            AppCache: false,
            publicPath: program.prefixLinks ? `${siteDB().get(`config`).linkPrefix}/` : `/`,
            relativePaths: false,
            ServiceWorker: {
              events: true,
            },
          }),
          new HardSourceWebpackPlugin({
            cacheDirectory: `${process.cwd()}/.cache/[confighash]`,
            recordsPath: `${process.cwd()}/.cache/[confighash]/records.json`,
            configHash: (webpackConfig) => stage,
            environmentPaths: {
              root: process.cwd(),
              directories: ['node_modules'],
              files: ['package.json'],
            },
          }),
        ]
      }
      default:
        throw new Error(`The state requested ${stage} doesn't exist.`)
    }
  }

  function resolve () {
    return {
      extensions: [
        ``,
        `.js`,
        `.jsx`,
        `.cjsx`,
        `.coffee`,
      ],
      // Hierarchy of directories for Webpack to look for module.
      // First is the site directory.
      // Then in the special directory of isomorphic modules Gatsby ships with.
      // Then the site's node_modules directory
      root: [
        directory,
        path.resolve(__dirname, `..`, `isomorphic`),
      ],
      modulesDirectories: [
        `${directory}/node_modules`,
        `node_modules`,
      ],
    }
  }

  function devtool () {
    switch (stage) {
      case `develop`:
        return `eval`
      case `build-html`:
        return false
      case `build-javascript`:
        return `source-map`
      default:
        return false
    }
  }

  function module (config) {
    // Common config for every env.
    config.loader(`cjsx`, {
      test: /\.cjsx$/,
      loaders: [`coffee`, `cjsx`],
    })
    config.loader(`js`, {
      test: /\.jsx?$/, // Accept either .js or .jsx files.
      exclude: /(node_modules|bower_components)/,
      loader: `babel`,
      query: babelConfig(program, babelStage),
    })
    config.loader(`coffee`, {
      test: /\.coffee$/,
      loader: `coffee`,
    })
    config.loader(`json`, {
      test: /\.json$/,
      loaders: [`json`],
    })
    // "file" loader makes sure those assets end up in the `public` folder.
    // When you `import` an asset, you get its filename.
    config.loader(`file-loader`, {
      test: /\.(ico|eot|otf|webp|ttf)(\?.*)?$/,
      loader: 'file',
      query: {
        name: 'static/[name].[hash:8].[ext]'
      }
    })
    // "url" loader works just like "file" loader but it also embeds
    // assets smaller than specified size as data URLs to avoid requests.
    config.loader(`url-loader`, {
      test: /\.(svg|jpg|jpeg|png|gif|mp4|webm|wav|mp3|m4a|aac|oga)(\?.*)?$/,
      loader: 'url',
      query: {
        limit: 7500,
        name: 'static/[name].[hash:8].[ext]'
      }
    })
    // Font loader.
    config.loader(`woff`, {
      test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
      loader: `url`,
      query: {
        limit: 15000, // Set high limit for inlining fonts as they're in the
        // critical path for rendering.
        name: `static/[name].[hash:8].[ext]`,
        mimetype: `application/font-woff`,
      }
    })

    const cssModulesConf = `css?modules&minimize&importLoaders=1`
    const cssModulesConfDev =
      `${cssModulesConf}&sourceMap&localIdentName=[name]---[local]---[hash:base64:5]`

    switch (stage) {
      case `develop`:

        config.loader(`css`, {
          test: /\.css$/,
          exclude: /\.module\.css$/,
          loaders: [`style`, `css`, `postcss`],
        })

        // CSS modules
        config.loader(`cssModules`, {
          test: /\.module\.css$/,
          loaders: [`style`, cssModulesConfDev, `postcss`],
        })

        config.merge({
          postcss (wp) {
            return [
              require(`postcss-import`)({ addDependencyTo: wp }),
              require(`postcss-cssnext`)({ browsers: `last 2 versions` }),
              require(`postcss-browser-reporter`),
              require(`postcss-reporter`),
            ]
          },
        })
        return config

      case `build-css`:
        config.loader(`css`, {
          test: /\.css$/,
          loader: ExtractTextPlugin.extract([`css?minimize`, `postcss`]),
          exclude: /\.module\.css$/,
        })

        // CSS modules
        config.loader(`cssModules`, {
          test: /\.module\.css$/,
          loader: ExtractTextPlugin.extract(`style`, [cssModulesConf, `postcss`]),
        })
        config.merge({
          postcss: [
            require(`postcss-import`)(),
            require(`postcss-cssnext`)({
              browsers: `last 2 versions`,
            }),
          ],
        })
        return config

      case `build-html`:
        // We don't deal with CSS at all when building the HTML.
        // The 'null' loader is used to prevent 'module not found' errors.
        // On the other hand CSS modules loaders are necessary.

        config.loader(`css`, {
          test: /\.css$/,
          exclude: /\.module\.css$/,
          loader: `null`,
        })

        // CSS modules
        config.loader(`cssModules`, {
          test: /\.module\.css$/,
          loader: ExtractTextPlugin.extract(`style`, [cssModulesConf, `postcss`]),
        })

        return config

      case `build-javascript`:
        // we don't deal with css at all when building the javascript.  but
        // still need to process the css so offline-plugin knows about the
        // various assets referenced in your css.
        //
        // It's also necessary to process CSS Modules so your JS knows the
        // classNames to use.

        config.loader(`css`, {
          test: /\.css$/,
          exclude: /\.module\.css$/,
          //loader: `null`,
          loader: ExtractTextPlugin.extract([`css`]),
        })

        // CSS modules
        config.loader(`cssModules`, {
          test: /\.module\.css$/,
          loader: ExtractTextPlugin.extract(`style`, [cssModulesConf, `postcss`]),
        })

        return config

      default:
        return config
    }
  }

  function resolveLoader () {
    const root = [
      path.resolve(directory, `node_modules`),
    ]

    const userLoaderDirectoryPath = path.resolve(directory, 'loaders')

    try {
      if (fs.statSync(userLoaderDirectoryPath).isDirectory()) {
        root.push(userLoaderDirectoryPath)
      }
    } catch (e) {
      if (e && e.code !== `ENOENT`) {
        console.log(e)
      }
    }

    return {
      root,
      modulesDirectories: [
        `node_modules`,
      ],
    }
  }

  const config = new Config()

  config.merge({
    // Context is the base directory for resolving the entry option.
    context: `${directory}`,
    node: {
      __filename: true,
    },
    entry: entry(),
    debug: true,
    // Certain "isomorphic" packages have different entry points for browser and server
    // (see https://github.com/defunctzombie/package-browser-field-spec);
    // setting the target tells webpack which file to include, ie. browser vs main.
    target: stage === `build-html` ? `node` : `web`,
    profile: stage === `production`,
    devtool: devtool(),
    output: output(),
    resolveLoader: resolveLoader(),
    plugins: plugins(),
    resolve: resolve(),
  })

  return webpackModifyValidate(module, config, stage)
}
<|endoftext|>


<commit_before>module.exports = {
  name: 'react-static',
  favicon: 'favicon.ico',
  cover: 'cover.png',
  repository: 'https://github.com/nozzle/react-static',
  license: 'MIT',
  root: './docs',
  logo: './logo.png',
  title: 'react-static',
  syntax: {
    showLineNumbers: true,
    highlighter: 'hljs',
    theme: 'atom-one-light',
    languages: ['javascript'],
  },
  sidebar: {
    items: [
      {
        name: 'Introduction',
        src: '../README.md',
      },
      {
        name: 'Core Concepts',
        src: 'concepts.md',
        children: [
          { name: 'Overview', src: 'concepts.md#overview' },
          {
            name: 'Code, Data, and Prop Splitting',
            src: 'concepts.md#code-data-and-prop-splitting',
          },
          {
            name: 'CSS and CSS-in-JS',
            src: 'concepts.md#css-and-css-in-js',
          },
          {
            name: 'Writing universal, "node-safe" code',
            src: 'concepts.md#writing-universal-node-safe-code',
          },
          { name: 'Environment Variables', src: 'concepts.md#environment-variables' },
          {
            name: 'Building your site for production',
            src: 'concepts.md#building-your-site-for-production',
          },
          { name: 'Continuous Integration', src: 'concepts.md#continuous-integration' },
          { name: 'Hosting', src: 'concepts.md#hosting' },
          { name: 'Using a CMS', src: 'concepts.md#using-a-cms' },
          {
            name: 'Rebuilding your site with Webhooks',
            src: 'concepts.md#rebuilding-your-site-with-webhooks',
          },
          { name: '404 Handling', src: 'concepts.md#404-handling' },
          { name: 'Non-Static Routing', src: 'concepts.md#non-static-routing' },
          {
            name: 'Webpack Customization and Plugins',
            src: 'concepts.md#webpack-customization-and-plugins',
          },
          {
            name: 'Using Preact in Production',
            src: 'concepts.md#using-preact-in-production',
          },
          { name: 'Pagination', src: 'concepts.md#pagination' },
          { name: 'Browser Support', src: 'concepts.md#browser-support' },
        ],
      },
      {
        name: 'API Reference',
        src: 'api.md',
        children: [
          {
            name: 'static.config.js',
            src: 'config.md',
          },
          {
            name: 'CLI',
            src: 'cli.md',
          },
          {
            name: 'Node API',
            src: 'node-api.md',
          },
          {
            name: 'Components',
            src: 'components.md',
          },
          {
            name: 'Methods',
            src: 'methods.md',
          },
        ],
      },
      {
        name: 'Changelog',
        src: '../CHANGELOG.md',
      },
      {
        name: 'Contributing',
        src: '../CONTRIBUTING.md',
      },
    ],
  },
}
<commit_msg>Update Docs<commit_after>module.exports = {
  name: 'react-static',
  favicon: 'favicon.ico',
  cover: 'cover.png',
  repository: 'https://github.com/nozzle/react-static',
  license: 'MIT',
  root: './docs',
  logo: './logo.png',
  title: 'react-static',
  syntax: {
    showLineNumbers: true,
    highlighter: 'hljs',
    theme: 'atom-one-light',
    languages: ['javascript'],
  },
  sidebar: {
    items: [
      {
        name: 'Introduction',
        src: '../README.md',
      },
      {
        name: 'Core Concepts',
        src: 'concepts.md',
        children: [
          { name: 'Overview', src: 'concepts.md#overview' },
          {
            name: 'Code, Data, and Prop Splitting',
            src: 'concepts.md#code-data-and-prop-splitting',
          },
          {
            name: 'CSS and CSS-in-JS',
            src: 'concepts.md#css-and-css-in-js',
          },
          {
            name: 'Writing universal, "node-safe" code',
            src: 'concepts.md#writing-universal-node-safe-code',
          },
          { name: 'Environment Variables', src: 'concepts.md#environment-variables' },
          {
            name: 'Building your site for production',
            src: 'concepts.md#building-your-site-for-production',
          },
          { name: 'Continuous Integration', src: 'concepts.md#continuous-integration' },
          { name: 'Hosting', src: 'concepts.md#hosting' },
          { name: 'Using a CMS', src: 'concepts.md#using-a-cms' },
          {
            name: 'Rebuilding your site with Webhooks',
            src: 'concepts.md#rebuilding-your-site-with-webhooks',
          },
          { name: '404 Handling', src: 'concepts.md#404-handling' },
          { name: 'Non-Static Routing', src: 'concepts.md#non-static-routing' },
          {
            name: 'Webpack Customization and Plugins',
            src: 'concepts.md#webpack-customization-and-plugins',
          },
          {
            name: 'Using Preact in Production',
            src: 'concepts.md#using-preact-in-production',
          },
          { name: 'Pagination', src: 'concepts.md#pagination' },
          { name: 'Browser Support', src: 'concepts.md#browser-support' },
        ],
      },
      {
        name: 'API Reference',
        children: [
          {
            name: 'static.config.js',
            src: 'config.md',
          },
          {
            name: 'CLI',
            src: 'cli.md',
          },
          {
            name: 'Node API',
            src: 'node-api.md',
          },
          {
            name: 'Components',
            src: 'components.md',
          },
          {
            name: 'Methods',
            src: 'methods.md',
          },
        ],
      },
      {
        name: 'Changelog',
        src: '../CHANGELOG.md',
      },
      {
        name: 'Contributing',
        src: '../CONTRIBUTING.md',
      },
    ],
  },
}
<|endoftext|>


<commit_before>
// TODO: extract to library
function lerp(k, from, to) {
  return from + k * (to - from);
}

// TODO: extract to library
var extend = function(obj) {
  var length = arguments.length;
  if (length < 2 || obj == null) {
    return obj;
  }

  for (var index = 1; index < length; index++) {
    var source = arguments[index],
      keys = Object.keys(source),
      l = keys.length;
    for (var i = 0; i < l; i++) {
      var key = keys[i];
      obj[key] = source[key];
    }
  }
  return obj;
};

var defaultTransition = {
  from: 0,
  to: 1,
  duration: 1,
  updateInterval: 0.01,
  loop: false
};

var Animation = function(transition) {
  var trans = extend({}, defaultTransition, transition || {});
  this._queue = [trans];
  this._qi = 0;
  this._phase = 0;
  this._intervalID = null;
  this._lastUpdate = null;
  this._reversed = false;
};

Animation.prototype.queue = function(transition) {
  var trans = extend(
    {}, this._queue[this._queue.length - 1], transition || {});
  this._queue.push(trans);
  return this;
};

Animation.prototype.play = function() {
  if (!this._intervalID) {
    this._setInterval();
  }

  return this;
};

Animation.prototype.reverse = function() {
  this._reversed = !this._reversed;
  return this;
};

Animation.prototype.stop = function(opts) {
  opts = opts || {};
  var skip = opts.skip || true;
  if (this._reversed) {
    this._phase = 0;
    this._qi = 0;
    if (skip) {
      this.emit('update', this._queue[this._qi].from);
    }
  } else {
    this._phase = 1;
    this._qi = this._queue.length - 1;
    if (skip) {
      this.emit('update', this._queue[this._qi].to);
    }
  }

  this._clearInterval();
  return this;
};

Animation.prototype._setInterval = function() {
  this._lastUpdate = getTime();
  this._update();
  this._intervalID = setInterval(
    this._update.bind(this),
    this._queue[this._qi].updateInterval * 1000);
};

Animation.prototype._clearInterval = function() {
  if (this._intervalID) {
    clearInterval(this._intervalID);
    this._intervalID = null;
  }
};

Animation.prototype._update = function() {
  var t = getTime();
  var dt = t - this._lastUpdate;
  this._lastUpdate = t;

  var trans = this._queue[this._qi];
  var dphase = dt / trans.duration;
  var phase = this._reversed ? (1 - this._phase) : this._phase;
  var qlast = this._queue.length - 1;
  var qi = this._reversed ? (qlast - this._qi) : this._qi;
  phase += dphase;

  var tweakIval = false;
  if (phase > 1) {
    // phase overflow
    if (trans.loop) {
      // we're in loop transition, just wrap around
      phase -= Math.floor(phase);
    } else if (this._qi < qlast) {
      // we have subsequent transition
      phase -= Math.floor(phase);
      ++qi;
      tweakIval = true;
    } else {
      // animation completed
      phase = 1;
      this._clearInterval();
    }
  }

  this._phase = this._reversed ? (1 - phase) : phase;
  this._qi = this._reversed ? (qlast - qi) : qi;
  if (tweakIval) {
    changeInterval(
      this._intervalID,
      this._queue[this._qi].updateInterval);
  }

  var val = lerp(this._phase, trans.from, trans.to);
  this.emit('update', val);
};

exports.create = function(transition) {
  return new Animation(transition);
};
<commit_msg>delete opts, add skip<commit_after>
// TODO: extract to library
function lerp(k, from, to) {
  return from + k * (to - from);
}

// TODO: extract to library
var extend = function(obj) {
  var length = arguments.length;
  if (length < 2 || obj == null) {
    return obj;
  }

  for (var index = 1; index < length; index++) {
    var source = arguments[index],
      keys = Object.keys(source),
      l = keys.length;
    for (var i = 0; i < l; i++) {
      var key = keys[i];
      obj[key] = source[key];
    }
  }
  return obj;
};

var defaultTransition = {
  from: 0,
  to: 1,
  duration: 1,
  updateInterval: 0.01,
  loop: false
};

var Animation = function(transition) {
  var trans = extend({}, defaultTransition, transition || {});
  this._queue = [trans];
  this._qi = 0;
  this._phase = 0;
  this._intervalID = null;
  this._lastUpdate = null;
  this._reversed = false;
};

Animation.prototype.queue = function(transition) {
  var trans = extend(
    {}, this._queue[this._queue.length - 1], transition || {});
  this._queue.push(trans);
  return this;
};

Animation.prototype.play = function() {
  if (!this._intervalID) {
    this._setInterval();
  }

  return this;
};

Animation.prototype.reverse = function() {
  this._reversed = !this._reversed;
  return this;
};

Animation.prototype.stop = function(skip) {
  skip = skip || true;
  if (this._reversed) {
    this._phase = 0;
    this._qi = 0;
    if (skip) {
      this.emit('update', this._queue[this._qi].from);
    }
  } else {
    this._phase = 1;
    this._qi = this._queue.length - 1;
    if (skip) {
      this.emit('update', this._queue[this._qi].to);
    }
  }

  this._clearInterval();
  return this;
};

Animation.prototype._setInterval = function() {
  this._lastUpdate = getTime();
  this._update();
  this._intervalID = setInterval(
    this._update.bind(this),
    this._queue[this._qi].updateInterval * 1000);
};

Animation.prototype._clearInterval = function() {
  if (this._intervalID) {
    clearInterval(this._intervalID);
    this._intervalID = null;
  }
};

Animation.prototype._update = function() {
  var t = getTime();
  var dt = t - this._lastUpdate;
  this._lastUpdate = t;

  var trans = this._queue[this._qi];
  var dphase = dt / trans.duration;
  var phase = this._reversed ? (1 - this._phase) : this._phase;
  var qlast = this._queue.length - 1;
  var qi = this._reversed ? (qlast - this._qi) : this._qi;
  phase += dphase;

  var tweakIval = false;
  if (phase > 1) {
    // phase overflow
    if (trans.loop) {
      // we're in loop transition, just wrap around
      phase -= Math.floor(phase);
    } else if (this._qi < qlast) {
      // we have subsequent transition
      phase -= Math.floor(phase);
      ++qi;
      tweakIval = true;
    } else {
      // animation completed
      phase = 1;
      this._clearInterval();
    }
  }

  this._phase = this._reversed ? (1 - phase) : phase;
  this._qi = this._reversed ? (qlast - qi) : qi;
  if (tweakIval) {
    changeInterval(
      this._intervalID,
      this._queue[this._qi].updateInterval);
  }

  var val = lerp(this._phase, trans.from, trans.to);
  this.emit('update', val);
};

exports.create = function(transition) {
  return new Animation(transition);
};
<|endoftext|>


<commit_before>#!/usr/bin/env node
"use strict";

// Dependencies
const Tilda = require("tilda")
    , Bloggify = require("..")
    , inquirer = require("inquirer")
    , fullname = require("fullname")
    , slug = require("slug")
    , log = require("emoji-logger")
    ;

new Tilda(`${__dirname}/../package.json`).main(action => {
    const MAIN_MENU = {
        title: "What do you want to do?",
        choices: [
            {
                name: "New Application"
              , value: "new-application"
              , choices: [
                    {
                        name: "Create a blog for me"
                      , value: "personal-blog"
                    }
                  , {
                        name: "Create a blog for somebody else"
                      , value: "blog"
                    }
                  , {
                        name: "Create a custom application"
                      , value: "custom-app"
                    }
                  , {
                        name: "« Back",
                        value: "_back"
                    }
                ]
            },
            {
                name: "Application Database Migrations"
              , value: "application-database-migrations"
              , choices: [
                    {
                        name: "Run migrations"
                      , value: "run-migrations"
                    }
                  , {
                        name: "Create migration"
                      , value: "create-migration"
                    }
                  , {
                        name: "« Back",
                        value: "_back"
                    }
                ]
            },
            {
                name: "Application Functions"
              , value: "application-functions"
              , choices: [
                    {
                        name: "Start Application"
                      , value: "application-start"
                      , choices: [
                            {
                                name: "Development"
                              , value: "application-start-development"
                            }
                          , {
                                name: "Debug"
                              , value: "application-start-debug"
                            }
                          , {
                                name: "Production"
                              , value: "application-start-production"
                            }
                          , {
                                name: "« Back",
                                value: "_back"
                            }
                        ]
                    }
                  , {
                        name: "Bundle Assets"
                      , value: "application-bundle"
                    }
                  , {
                        name: "Database Connect"
                      , value: "database-connect"
                    }
                  , {
                        name: "« Back",
                        value: "_back"
                    }
                ]
            }
        ]
    }

    const processMenu = async () => {

        let parent  = MAIN_MENU

        do {
            parent.choices = parent.choices || []
            const currentMenu = {
                type: "list",
                name: "main",
                message: parent.title || parent.name,
                choices: parent.choices.map(c => {
                    return {
                        name: c.name,
                        value: c.value
                    }
                })
            }

            const choiceMap = parent.choices.reduce((acc, c) => {
                acc[c.value] = c
                return acc
            }, {})

            const answer = (await inquirer.prompt([currentMenu])).main
            const answerMenu = choiceMap[answer]
            answerMenu.choices = answerMenu.choices || []

            if (answerMenu.choices.length) {
                answerMenu._parent = parent
                parent = answerMenu
            } else if (answer === "_back") {
                parent = parent._parent
            } else {
                return answer
            }
        } while (true)
    }

    processMenu().then(answer => {
        switch (answer) {
            case "blog":
            case "personal-blog":
            case "custom-app":
                log("So, you want to create a new Bloggify app! Cool. Please answer the following questions. If you're happy with the defaults (the one in parentheses), just press the return key.", "info");
                fullname().then(userFullName=> {
                    let questions = [
                        answer !== "custom-app" ? {
                            type: "input",
                            name: "full_name",
                            default: () => userFullName,
                            message: answer === "personal-blog" ? "Your name" : "Your friend's name"
                        } : null,
                        {
                            type: "input",
                            name: "git_url",
                            default: () => answer === "custom-app" ? "https://github.com/BloggifyTutorials/custom-app" : "https://github.com/Bloggify/bloggify-quick-start.git",
                            message: "Application Template"
                        },
                        {
                            type: "input",
                            name: "title",
                            default: answs => /blog/.test(answer) ? answs.full_name : "Custom App",
                            message: "Website title"
                        },
                        {
                            type: "input",
                            name: "description",
                            message: "Website description",
                            default: answs => `${/blog/.test(answer) ? answs.full_name + "'s Blog" : "My fancy Bloggify app."}`
                        },
                        answer !== "custom-app" ? {
                            type: "input",
                            name: "theme",
                            message: "Website theme",
                            default: () => `bloggify-theme-light`
                        } : null,
                        {
                            type: "input",
                            name: "domain",
                            message: "Production url",
                            default (answers) {
                                return `http://${slug(answers.title, { lower: true })}.com`;
                            }
                            // TODO Validation
                        }
                    ].filter(Boolean);
                    return inquirer.prompt(questions);
                }).then(answers => {
                    answers.dir = slug(answers.title, { lower: true });
                    Bloggify.init(answers);
                });
                break;

            case "application-start-development":
                Bloggify.applicationStart(Bloggify.APP_MODES.DEVELOPMENT)
                break;

            case "application-start-debug":
                Bloggify.applicationStart(Bloggify.APP_MODES.DEBUG)
                break;

            case "application-start-production":
                Bloggify.applicationStart(Bloggify.APP_MODES.PRODUCTION)
                break;

            case "database-connect":
                Bloggify.databaseConnect()
                break;

            case "application-bundle":
                Bloggify.applicationBundle()
                break;

            // Migrations
            case "run-migrations":
                Bloggify.runMigrations()
                break;

            // Migrations
            case "create-migration":
                inquirer.prompt([{
                    type: "input",
                    name: "migration_name",
                    message: "Migration name"
                }]).then(({ migration_name }) => {
                    Bloggify.createMigration(migration_name)
                })
                break;
        }
    });
});
<commit_msg>add the custom app with sqlite<commit_after>#!/usr/bin/env node
"use strict";

// Dependencies
const Tilda = require("tilda")
    , Bloggify = require("..")
    , inquirer = require("inquirer")
    , fullname = require("fullname")
    , slug = require("slug")
    , log = require("emoji-logger")
    ;

new Tilda(`${__dirname}/../package.json`).main(async action => {
    const MAIN_MENU = {
        title: "What do you want to do?",
        choices: [
            {
                name: "New Application"
              , value: "new-application"
              , choices: [
                    {
                        name: "Create a blog for me"
                      , value: "personal-blog"
                    }
                  , {
                        name: "Create a blog for somebody else"
                      , value: "blog"
                    }
                  , {
                        name: "Create a custom application"
                      , value: "custom-app"
                    }
                  , {
                        name: "Create a custom application with Sqlite database"
                      , value: "custom-app-sqlite"
                    }
                  , {
                        name: "« Back",
                        value: "_back"
                    }
                ]
            },
            {
                name: "Application Database Migrations"
              , value: "application-database-migrations"
              , choices: [
                    {
                        name: "Run migrations"
                      , value: "run-migrations"
                    }
                  , {
                        name: "Create migration"
                      , value: "create-migration"
                    }
                  , {
                        name: "« Back",
                        value: "_back"
                    }
                ]
            },
            {
                name: "Application Functions"
              , value: "application-functions"
              , choices: [
                    {
                        name: "Start Application"
                      , value: "application-start"
                      , choices: [
                            {
                                name: "Development"
                              , value: "application-start-development"
                            }
                          , {
                                name: "Debug"
                              , value: "application-start-debug"
                            }
                          , {
                                name: "Production"
                              , value: "application-start-production"
                            }
                          , {
                                name: "« Back",
                                value: "_back"
                            }
                        ]
                    }
                  , {
                        name: "Bundle Assets"
                      , value: "application-bundle"
                    }
                  , {
                        name: "Database Connect"
                      , value: "database-connect"
                    }
                  , {
                        name: "« Back",
                        value: "_back"
                    }
                ]
            }
        ]
    }

    const processMenu = async () => {

        let parent  = MAIN_MENU

        do {
            parent.choices = parent.choices || []
            const currentMenu = {
                type: "list",
                name: "main",
                message: parent.title || parent.name,
                choices: parent.choices.map(c => {
                    return {
                        name: c.name,
                        value: c.value
                    }
                })
            }

            const choiceMap = parent.choices.reduce((acc, c) => {
                acc[c.value] = c
                return acc
            }, {})

            const answer = (await inquirer.prompt([currentMenu])).main
            const answerMenu = choiceMap[answer]
            answerMenu.choices = answerMenu.choices || []

            if (answerMenu.choices.length) {
                answerMenu._parent = parent
                parent = answerMenu
            } else if (answer === "_back") {
                parent = parent._parent
            } else {
                return answer
            }
        } while (true)
    }

    const answer = await processMenu()
    switch (answer) {
        case "blog":
        case "personal-blog":
        case "custom-app":
        case "custom-app-sqlite":
            log("So, you want to create a new Bloggify app! Cool. Please answer the following questions. If you're happy with the defaults (the one in parentheses), just press the return key.", "info");
            const userFullName = await fullname()
            const isCustom = answer.includes("custom-app")
            const defaultGitUrl = ({
                "custom-app": "https://github.com/BloggifyTutorials/custom-app",
                "custom-app-sqlite": "https://github.com/BloggifyTutorials/custom-app-sqlite",
                "personal-blog": "https://github.com/Bloggify/bloggify-quick-start.git",
                "blog": "https://github.com/Bloggify/bloggify-quick-start.git"
            })[answer]

            let questions = [

                // Get the fullname
                !isCustom ? {
                    type: "input",
                    name: "full_name",
                    default: () => userFullName,
                    message: answer === "personal-blog" ? "Your name" : "Your friend's name"
                } : null,

                // Set the template git url
                {
                    type: "input",
                    name: "git_url",
                    default: () => defaultGitUrl,
                    message: "Application Template"
                },

                // App title
                {
                    type: "input",
                    name: "title",
                    default: answs => /blog/.test(answer) ? answs.full_name : "Custom App",
                    message: "Website title"
                },

                // App description
                {
                    type: "input",
                    name: "description",
                    message: "Website description",
                    default: answs => `${/blog/.test(answer) ? answs.full_name + "'s Blog" : "My fancy Bloggify app."}`
                },

                // Theme
                !isCustom ? {
                    type: "input",
                    name: "theme",
                    message: "Website theme",
                    default: () => `bloggify-theme-light`
                } : null,

                // Production Url
                {
                    type: "input",
                    name: "domain",
                    message: "Production url",
                    default (answers) {
                        return `https://${slug(answers.title, { lower: true })}.com`;
                    }
                    // TODO Validation
                }
            ].filter(Boolean);
            const answers = await inquirer.prompt(questions);
            answers.dir = slug(answers.title, { lower: true });
            Bloggify.init(answers);
            break;
        case "application-start-development":
            Bloggify.applicationStart(Bloggify.APP_MODES.DEVELOPMENT)
            break;

        case "application-start-debug":
            Bloggify.applicationStart(Bloggify.APP_MODES.DEBUG)
            break;

        case "application-start-production":
            Bloggify.applicationStart(Bloggify.APP_MODES.PRODUCTION)
            break;

        case "database-connect":
            Bloggify.databaseConnect()
            break;

        case "application-bundle":
            Bloggify.applicationBundle()
            break;

        // Migrations
        case "run-migrations":
            Bloggify.runMigrations()
            break;

        // Migrations
        case "create-migration":
            const { migration_name } = await inquirer.prompt([{
                type: "input",
                name: "migration_name",
                message: "Migration name"
            }])
            Bloggify.createMigration(migration_name)
            break;
    }
});
<|endoftext|>


<commit_before>import FakePlayback from '../../src/base/playback'
import Container from '../../src/components/container'
import Poster from '../../src/plugins/poster'
import Events from '../../src/base/events'
import $ from 'clappr-zepto'

describe('Poster', function() {
  beforeEach(function() {
    this.playback = new FakePlayback()
    this.container = new Container({playback: this.playback})
    this.poster = new Poster(this.container)
    this.container.addPlugin(this.poster)
  })

  it('is named poster', function() {
    expect(this.poster.name).to.equal('poster')
  })

  it('disables media control by default', function() {
    expect(this.container.mediaControlDisabled).to.be.true
  })

  it('listens to container:state:buffering event', function() {
    sinon.spy(this.poster, 'hidePlayButton')
    this.container.trigger(Events.CONTAINER_STATE_BUFFERING)
    expect(this.poster.hidePlayButton).called.once
    expect(this.poster.$playButton.is(':visible')).to.be.false
  })

  it('listens to container:bufferfull event', function() {
    sinon.spy(this.poster, 'onBufferfull')
    this.poster.bindEvents()
    this.container.trigger(Events.CONTAINER_STATE_BUFFERFULL)

    expect(this.poster.onBufferfull).called.once
    expect(this.poster.$el.is(':visible')).to.be.false
  })

  it('listens to container:stop event', function() {
    sinon.spy(this.container, 'disableMediaControl')
    sinon.spy(this.poster, 'showPlayButton')
    this.container.trigger(Events.CONTAINER_STOP)

    expect(this.container.disableMediaControl).called.once
    expect(this.poster.showPlayButton).called.once

    sinon.spy(this.poster, 'onStop')
    this.poster.bindEvents()

    this.container.trigger(Events.CONTAINER_STOP)

    expect(this.poster.onStop).called.once
  })

  it('treats container:ended event as container:stop', function() {
    sinon.spy(this.container, 'disableMediaControl')
    sinon.spy(this.poster, 'showPlayButton')
    this.container.trigger(Events.CONTAINER_ENDED)

    expect(this.container.disableMediaControl).called.once
    expect(this.poster.showPlayButton).called.once

    var spy = sinon.spy(this.poster, 'onStop')
    this.poster.bindEvents()

    this.container.trigger(Events.CONTAINER_STOP)

    expect(spy).called.once
  })

  it('plays the container on click', function() {
    sinon.spy(this.container, 'play')
    $(this.poster.$el).click()
    expect(this.container.play).called.once
  })
})
<commit_msg>Removed no longer relevant tests<commit_after>import FakePlayback from '../../src/base/playback'
import Container from '../../src/components/container'
import Poster from '../../src/plugins/poster'
import Events from '../../src/base/events'
import $ from 'clappr-zepto'

describe('Poster', function() {
  beforeEach(function() {
    this.playback = new FakePlayback()
    this.container = new Container({playback: this.playback})
    this.poster = new Poster(this.container)
    this.container.addPlugin(this.poster)
  })

  it('is named poster', function() {
    expect(this.poster.name).to.equal('poster')
  })

  it('disables media control by default', function() {
    expect(this.container.mediaControlDisabled).to.be.true
  })

  it('listens to container:stop event', function() {
    sinon.spy(this.container, 'disableMediaControl')
    sinon.spy(this.poster, 'showPlayButton')
    this.container.trigger(Events.CONTAINER_STOP)

    expect(this.container.disableMediaControl).called.once
    expect(this.poster.showPlayButton).called.once

    sinon.spy(this.poster, 'onStop')
    this.poster.bindEvents()

    this.container.trigger(Events.CONTAINER_STOP)

    expect(this.poster.onStop).called.once
  })

  it('treats container:ended event as container:stop', function() {
    sinon.spy(this.container, 'disableMediaControl')
    sinon.spy(this.poster, 'showPlayButton')
    this.container.trigger(Events.CONTAINER_ENDED)

    expect(this.container.disableMediaControl).called.once
    expect(this.poster.showPlayButton).called.once

    var spy = sinon.spy(this.poster, 'onStop')
    this.poster.bindEvents()

    this.container.trigger(Events.CONTAINER_STOP)

    expect(spy).called.once
  })

  it('plays the container on click', function() {
    sinon.spy(this.container, 'play')
    $(this.poster.$el).click()
    expect(this.container.play).called.once
  })
})
<|endoftext|>