<commit_before>'use strict';

const inquirer = require('inquirer');
const run = require('./run');
const getProjectOptions = require('./get-project-options');
const getPackageName = require('./get-package-name');
const getPackageVersion = require('./get-package-version');
const getVersions = require('boilerplate-update/src/get-versions');
const getProjectVersion = require('./get-project-version');
const _getTagVersion = require('./get-tag-version');
const getRemoteUrl = require('./get-remote-url');
const boilerplateUpdate = require('boilerplate-update');
const getStartAndEndCommands = require('./get-start-and-end-commands');
const parseBlueprint = require('./parse-blueprint');
const downloadBlueprint = require('./download-blueprint');
const loadSafeBlueprintFile = require('./load-safe-blueprint-file');
const saveBlueprint = require('./save-blueprint');

module.exports = async function emberCliUpdate({
  blueprint: _blueprint,
  from,
  to,
  resolveConflicts,
  runCodemods,
  reset,
  compareOnly,
  statsOnly,
  listCodemods,
  createCustomDiff,
  wasRunAsExecutable
}) {
  let defaultBlueprint = {
    name: 'ember-cli'
  };

  let blueprint;

  if (_blueprint) {
    if (!from) {
      throw new Error('A custom blueprint cannot detect --from. You must supply it.');
    }

    blueprint = await parseBlueprint(_blueprint);
    blueprint.version = from;
  } else {
    let { blueprints } = await loadSafeBlueprintFile(process.cwd());

    let completeBlueprints = blueprints.filter(blueprint => !blueprint.isPartial);
    if (!completeBlueprints.length) {
      blueprints.splice(0, 0, defaultBlueprint);
    }

    if (blueprints.length > 1) {
      let answers = await inquirer.prompt([{
        type: 'list',
        message: 'Multiple blueprint updates have been found. Which would you like to update?',
        name: 'blueprint',
        choices: blueprints.map(blueprint => blueprint.name)
      }]);

      blueprint = blueprints.find(blueprint => blueprint.name === answers.blueprint);
    } else {
      blueprint = blueprints[0];
    }

    if (blueprint.location) {
      blueprint.url = (await parseBlueprint(blueprint.location)).url;
    }
  }

  let isCustomBlueprint = blueprint.name !== defaultBlueprint.name;

  if (isCustomBlueprint) {
    createCustomDiff = true;
  }

  let endVersion;

  let result = await (await boilerplateUpdate({
    projectOptions: ({ packageJson }) => getProjectOptions(packageJson, blueprint),
    mergeOptions: async function mergeOptions({
      packageJson,
      projectOptions
    }) {
      let startVersion;
      let startBlueprint;
      let endBlueprint;

      if (isCustomBlueprint) {
        startBlueprint = await downloadBlueprint(blueprint.name, blueprint.url, blueprint.version);
        endBlueprint = await downloadBlueprint(blueprint.name, blueprint.url, to);

        startVersion = startBlueprint.version;
        endVersion = endBlueprint.version;

        blueprint.name = startBlueprint.name;
      } else {
        let packageName = getPackageName(projectOptions);
        let packageVersion = getPackageVersion(packageJson, packageName);

        let versions = await getVersions(packageName);

        let getTagVersion = _getTagVersion(versions, packageName);

        if (from) {
          startVersion = await getTagVersion(from);
        } else {
          startVersion = getProjectVersion(packageVersion, versions, projectOptions);
        }

        endVersion = await getTagVersion(to);

        startBlueprint = endBlueprint = blueprint;
      }

      let customDiffOptions;
      if (createCustomDiff) {
        customDiffOptions = getStartAndEndCommands({
          packageJson,
          projectOptions,
          startVersion,
          endVersion,
          startBlueprint,
          endBlueprint
        });
      }

      return {
        startVersion,
        endVersion,
        customDiffOptions
      };
    },
    remoteUrl: ({ projectOptions }) => getRemoteUrl(projectOptions),
    compareOnly,
    resolveConflicts,
    reset,
    statsOnly,
    listCodemods,
    runCodemods,
    codemodsUrl: 'https://raw.githubusercontent.com/ember-cli/ember-cli-update-codemods-manifest/v3/manifest.json',
    createCustomDiff,
    ignoredFiles: ['ember-cli-update.json'],
    wasRunAsExecutable
  })).promise;

  let emberCliUpdateJson = await loadSafeBlueprintFile(process.cwd());

  let existingBlueprint = emberCliUpdateJson.blueprints.find(b => b.name === blueprint.name);

  if (existingBlueprint) {
    await saveBlueprint({
      cwd: process.cwd(),
      name: blueprint.name,
      version: endVersion
    });

    await run('git add ember-cli-update.json');
  }

  return result;
};
<commit_msg>refactor<commit_after>'use strict';

const inquirer = require('inquirer');
const run = require('./run');
const getProjectOptions = require('./get-project-options');
const getPackageName = require('./get-package-name');
const getPackageVersion = require('./get-package-version');
const getVersions = require('boilerplate-update/src/get-versions');
const getProjectVersion = require('./get-project-version');
const _getTagVersion = require('./get-tag-version');
const getRemoteUrl = require('./get-remote-url');
const boilerplateUpdate = require('boilerplate-update');
const getStartAndEndCommands = require('./get-start-and-end-commands');
const parseBlueprint = require('./parse-blueprint');
const downloadBlueprint = require('./download-blueprint');
const loadSafeBlueprintFile = require('./load-safe-blueprint-file');
const saveBlueprint = require('./save-blueprint');

module.exports = async function emberCliUpdate({
  blueprint: _blueprint,
  from,
  to,
  resolveConflicts,
  runCodemods,
  reset,
  compareOnly,
  statsOnly,
  listCodemods,
  createCustomDiff,
  wasRunAsExecutable
}) {
  let defaultBlueprint = {
    name: 'ember-cli'
  };

  let blueprint;

  if (_blueprint) {
    if (!from) {
      throw new Error('A custom blueprint cannot detect --from. You must supply it.');
    }

    blueprint = await parseBlueprint(_blueprint);
    blueprint.version = from;
  } else {
    let { blueprints } = await loadSafeBlueprintFile(process.cwd());

    let completeBlueprints = blueprints.filter(blueprint => !blueprint.isPartial);
    if (!completeBlueprints.length) {
      blueprints.splice(0, 0, defaultBlueprint);
    }

    if (blueprints.length > 1) {
      let answers = await inquirer.prompt([{
        type: 'list',
        message: 'Multiple blueprint updates have been found. Which would you like to update?',
        name: 'blueprint',
        choices: blueprints.map(blueprint => blueprint.name)
      }]);

      blueprint = blueprints.find(blueprint => blueprint.name === answers.blueprint);
    } else {
      blueprint = blueprints[0];
    }

    if (blueprint.location) {
      blueprint.url = (await parseBlueprint(blueprint.location)).url;
    }
  }

  let isCustomBlueprint = blueprint.name !== defaultBlueprint.name;

  if (isCustomBlueprint) {
    createCustomDiff = true;
  }

  let endVersion;

  let result = await (await boilerplateUpdate({
    projectOptions: ({ packageJson }) => getProjectOptions(packageJson, blueprint),
    mergeOptions: async function mergeOptions({
      packageJson,
      projectOptions
    }) {
      let startVersion;
      let startBlueprint;
      let endBlueprint;

      if (isCustomBlueprint) {
        startBlueprint = await downloadBlueprint(blueprint.name, blueprint.url, blueprint.version);
        endBlueprint = await downloadBlueprint(blueprint.name, blueprint.url, to);

        startVersion = startBlueprint.version;
        endVersion = endBlueprint.version;

        blueprint.name = startBlueprint.name;
      } else {
        let packageName = getPackageName(projectOptions);
        let packageVersion = getPackageVersion(packageJson, packageName);

        let versions = await getVersions(packageName);

        let getTagVersion = _getTagVersion(versions, packageName);

        if (from) {
          startVersion = await getTagVersion(from);
        } else {
          startVersion = getProjectVersion(packageVersion, versions, projectOptions);
        }

        endVersion = await getTagVersion(to);

        startBlueprint = endBlueprint = blueprint;
      }

      let customDiffOptions;
      if (createCustomDiff) {
        customDiffOptions = getStartAndEndCommands({
          packageJson,
          projectOptions,
          startVersion,
          endVersion,
          startBlueprint,
          endBlueprint
        });
      }

      return {
        startVersion,
        endVersion,
        customDiffOptions
      };
    },
    remoteUrl: ({ projectOptions }) => getRemoteUrl(projectOptions),
    compareOnly,
    resolveConflicts,
    reset,
    statsOnly,
    listCodemods,
    runCodemods,
    codemodsUrl: 'https://raw.githubusercontent.com/ember-cli/ember-cli-update-codemods-manifest/v3/manifest.json',
    createCustomDiff,
    ignoredFiles: ['ember-cli-update.json'],
    wasRunAsExecutable
  })).promise;

  let { blueprints } = await loadSafeBlueprintFile(process.cwd());

  let existingBlueprint = blueprints.find(b => b.name === blueprint.name);

  if (existingBlueprint) {
    await saveBlueprint({
      cwd: process.cwd(),
      name: blueprint.name,
      version: endVersion
    });

    await run('git add ember-cli-update.json');
  }

  return result;
};
<|endoftext|>


<commit_before>var DEFAULT_NODE = 0
var RECYCLED_NODE = 1
var LAZY_NODE = 2
var TEXT_NODE = 3
var EMPTY_OBJECT = {}
var EMPTY_ARRAY = []
var map = EMPTY_ARRAY.map
var isArray = Array.isArray
var defer = requestAnimationFrame || setTimeout

var createClass = function(obj) {
  var out = ""
  var tmp = typeof obj

  if (tmp === "string" || tmp === "number") return obj

  if (isArray(obj) && obj.length > 0) {
    for (var i = 0; i < obj.length; i++) {
      if ((tmp = createClass(obj[i])) !== "") out += (out && " ") + tmp
    }
  } else {
    for (var i in obj) {
      if (obj[i]) out += (out && " ") + i
    }
  }

  return out
}

var merge = function(a, b) {
  var out = {}

  for (var i in a) out[i] = a[i]
  for (var i in b) out[i] = b[i]

  return out
}

var flatten = function(arr) {
  return arr.reduce(function(out, obj) {
    return out.concat(
      !obj || obj === true
        ? false
        : typeof obj[0] === "function"
        ? [obj]
        : flatten(obj)
    )
  }, EMPTY_ARRAY)
}

var isSameAction = function(a, b) {
  return isArray(a) && isArray(b) && a[0] === b[0] && typeof a[0] === "function"
}

var shouldRestart = function(a, b) {
  for (var k in merge(a, b)) {
    if (a[k] === b[k] || isSameAction(a[k], b[k])) b[k] = a[k]
    else return true
  }
}

var patchSub = function(sub, newSub, dispatch) {
  for (var i = 0, a, b, out = []; i < sub.length || i < newSub.length; i++) {
    a = sub[i]
    out.push(
      (b = newSub[i])
        ? !a || b[0] !== a[0] || shouldRestart(b[1], a[1])
          ? [b[0], b[1], b[0](b[1], dispatch), a && a[2]()]
          : a
        : a && a[2]()
    )
  }
  return out
}

var updateProperty = function(element, name, value, newValue, eventCb, isSvg) {
  if (name === "key") {
  } else if (name === "style") {
    for (var i in merge(value, newValue)) {
      var style = newValue == null || newValue[i] == null ? "" : newValue[i]
      if (i[0] === "-") {
        element[name].setProperty(i, style)
      } else {
        element[name][i] = style
      }
    }
  } else if (name[0] === "o" && name[1] === "n") {
    if (
      !((element.events || (element.events = {}))[
        (name = name.slice(2))
      ] = newValue)
    ) {
      element.removeEventListener(name, eventCb)
    } else if (!value) {
      element.addEventListener(name, eventCb)
    }
  } else if (name !== "list" && !isSvg && name in element) {
    element[name] = newValue == null ? "" : newValue
  } else if (
    newValue == null ||
    newValue === false ||
    (name === "class" && !(newValue = createClass(newValue)))
  ) {
    element.removeAttribute(name)
  } else {
    element.setAttribute(name, newValue)
  }
}

var removeElement = function(parent, node) {
  parent.removeChild(node.element)
}

var createElement = function(node, eventCb, isSvg) {
  var element =
    node.type === TEXT_NODE
      ? document.createTextNode(node.name)
      : (isSvg = isSvg || node.name === "svg")
      ? document.createElementNS("http://www.w3.org/2000/svg", node.name)
      : document.createElement(node.name)
  var props = node.props

  for (var k in props) {
    updateProperty(element, k, null, props[k], eventCb, isSvg)
  }

  for (var i = 0, len = node.children.length; i < len; i++) {
    element.appendChild(
      createElement(
        (node.children[i] = getNode(node.children[i])),
        eventCb,
        isSvg
      )
    )
  }

  return (node.element = element)
}

var updateElement = function(element, props, newProps, eventCb, isSvg) {
  for (var k in merge(props, newProps)) {
    if (
      (k === "value" || k === "selected" || k === "checked"
        ? element[k]
        : props[k]) !== newProps[k]
    ) {
      updateProperty(element, k, props[k], newProps[k], eventCb, isSvg)
    }
  }
}

var getKey = function(node) {
  return node == null ? null : node.key
}

var patch = function(parent, element, node, newNode, eventCb, isSvg) {
  if (newNode === node) {
  } else if (
    node != null &&
    node.type === TEXT_NODE &&
    newNode.type === TEXT_NODE
  ) {
    if (node.name !== newNode.name) element.nodeValue = newNode.name
  } else if (node == null || node.name !== newNode.name) {
    var newElement = parent.insertBefore(
      createElement((newNode = getNode(newNode)), eventCb, isSvg),
      element
    )

    if (node != null) removeElement(parent, node)

    element = newElement
  } else {
    updateElement(
      element,
      node.props,
      newNode.props,
      eventCb,
      (isSvg = isSvg || newNode.name === "svg")
    )

    var savedNode
    var childNode

    var key
    var children = node.children
    var start = 0
    var end = children.length - 1

    var newKey
    var newChildren = newNode.children
    var newStart = 0
    var newEnd = newChildren.length - 1

    while (newStart <= newEnd && start <= end) {
      key = getKey(children[start])
      newKey = getKey(newChildren[newStart])

      if (key == null || key !== newKey) break

      patch(
        element,
        children[start].element,
        children[start],
        (newChildren[newStart] = getNode(
          newChildren[newStart],
          children[start]
        )),
        eventCb,
        isSvg
      )

      start++
      newStart++
    }

    while (newStart <= newEnd && start <= end) {
      key = getKey(children[end])
      newKey = getKey(newChildren[newEnd])

      if (key == null || key !== newKey) break

      patch(
        element,
        children[end].element,
        children[end],
        (newChildren[newEnd] = getNode(newChildren[newEnd], children[end])),
        eventCb,
        isSvg
      )

      end--
      newEnd--
    }

    if (start > end) {
      while (newStart <= newEnd) {
        element.insertBefore(
          createElement(
            (newChildren[newStart] = getNode(newChildren[newStart++])),
            eventCb,
            isSvg
          ),
          (childNode = children[start]) && childNode.element
        )
      }
    } else if (newStart > newEnd) {
      while (start <= end) {
        removeElement(element, children[start++])
      }
    } else {
      for (var i = start, keyed = {}, newKeyed = {}; i <= end; i++) {
        if ((key = children[i].key) != null) {
          keyed[key] = children[i]
        }
      }

      while (newStart <= newEnd) {
        key = getKey((childNode = children[start]))
        newKey = getKey(
          (newChildren[newStart] = getNode(newChildren[newStart], childNode))
        )

        if (
          newKeyed[key] ||
          (newKey != null && newKey === getKey(children[start + 1]))
        ) {
          if (key == null) {
            removeElement(element, childNode)
          }
          start++
          continue
        }

        if (newKey == null || node.type === RECYCLED_NODE) {
          if (key == null) {
            patch(
              element,
              childNode && childNode.element,
              childNode,
              newChildren[newStart],
              eventCb,
              isSvg
            )
            newStart++
          }
          start++
        } else {
          if (key === newKey) {
            patch(
              element,
              childNode.element,
              childNode,
              newChildren[newStart],
              eventCb,
              isSvg
            )
            newKeyed[newKey] = true
            start++
          } else {
            if ((savedNode = keyed[newKey]) != null) {
              patch(
                element,
                element.insertBefore(
                  savedNode.element,
                  childNode && childNode.element
                ),
                savedNode,
                newChildren[newStart],
                eventCb,
                isSvg
              )
              newKeyed[newKey] = true
            } else {
              patch(
                element,
                childNode && childNode.element,
                null,
                newChildren[newStart],
                eventCb,
                isSvg
              )
            }
          }
          newStart++
        }
      }

      while (start <= end) {
        if (getKey((childNode = children[start++])) == null) {
          removeElement(element, childNode)
        }
      }

      for (var key in keyed) {
        if (newKeyed[key] == null) {
          removeElement(element, keyed[key])
        }
      }
    }
  }

  return (newNode.element = element)
}

var shouldUpdate = function(a, b) {
  for (var k in a) if (a[k] !== b[k]) return true
  for (var k in b) if (a[k] !== b[k]) return true
}

var getNode = function(newNode, node) {
  return newNode.type === LAZY_NODE
    ? !node || shouldUpdate(newNode.lazy, node.lazy)
      ? newNode.render()
      : node
    : newNode
}

var createVNode = function(name, props, children, element, key, type) {
  return {
    name: name,
    props: props,
    children: children,
    element: element,
    type: type,
    key: key
  }
}

var createTextVNode = function(text, element) {
  return createVNode(text, EMPTY_OBJECT, EMPTY_ARRAY, element, null, TEXT_NODE)
}

var recycleElement = function(element) {
  return element.nodeType === TEXT_NODE
    ? createTextVNode(element.nodeValue, element)
    : createVNode(
        element.nodeName.toLowerCase(),
        EMPTY_OBJECT,
        map.call(element.childNodes, recycleElement),
        element,
        null,
        RECYCLED_NODE
      )
}

export var Lazy = function(props) {
  return {
    type: LAZY_NODE,
    key: props.key,
    lazy: props,
    render: function() {
      var node = props.render(props)
      node.lazy = props
      return node
    }
  }
}

export var h = function(name, props) {
  for (var node, rest = [], children = [], i = arguments.length; i-- > 2; ) {
    rest.push(arguments[i])
  }

  while (rest.length > 0) {
    if (isArray((node = rest.pop()))) {
      for (i = node.length; i-- > 0; ) rest.push(node[i])
    } else if (node === false || node === true || node == null) {
    } else {
      children.push(typeof node === "object" ? node : createTextVNode(node))
    }
  }

  props = props || EMPTY_OBJECT

  return typeof name === "function"
    ? name(props, children)
    : createVNode(name, props, children, null, props.key, DEFAULT_NODE)
}

export var app = function(props) {
  var container = props.container
  var element = container && container.children[0]
  var node = element && recycleElement(element)
  var subs = props.subscriptions
  var view = props.view
  var lock = false
  var state = {}
  var sub = []

  var eventCb = function(event) {
    dispatch(event.currentTarget.events[event.type], event)
  }

  var setState = function(newState) {
    if (!(state === newState || lock)) defer(render, (lock = true))
    state = newState
  }

  var dispatch = function(obj, props) {
    if (typeof obj === "function") {
      dispatch(obj(state, props))
    } else if (isArray(obj)) {
      if (typeof obj[0] === "function") {
        dispatch(obj[0](state, obj[1], props))
      } else {
        flatten(obj.slice(1)).map(function(fx) {
          fx && fx[0](fx[1], dispatch)
        }, setState(obj[0]))
      }
    } else {
      setState(obj)
    }
  }

  var render = function() {
    lock = false
    if (subs) sub = patchSub(sub, flatten(subs(state)), dispatch)
    if (view) {
      element = patch(container, element, node, (node = view(state)), eventCb)
    }
  }

  dispatch(props.init)
}
<commit_msg>Create Dispatch Decorator (#822)<commit_after>var DEFAULT_NODE = 0
var RECYCLED_NODE = 1
var LAZY_NODE = 2
var TEXT_NODE = 3
var EMPTY_OBJECT = {}
var EMPTY_ARRAY = []
var map = EMPTY_ARRAY.map
var isArray = Array.isArray
var defer = requestAnimationFrame || setTimeout

var createClass = function(obj) {
  var out = ""
  var tmp = typeof obj

  if (tmp === "string" || tmp === "number") return obj

  if (isArray(obj) && obj.length > 0) {
    for (var i = 0; i < obj.length; i++) {
      if ((tmp = createClass(obj[i])) !== "") out += (out && " ") + tmp
    }
  } else {
    for (var i in obj) {
      if (obj[i]) out += (out && " ") + i
    }
  }

  return out
}

var merge = function(a, b) {
  var out = {}

  for (var i in a) out[i] = a[i]
  for (var i in b) out[i] = b[i]

  return out
}

var flatten = function(arr) {
  return arr.reduce(function(out, obj) {
    return out.concat(
      !obj || obj === true
        ? false
        : typeof obj[0] === "function"
        ? [obj]
        : flatten(obj)
    )
  }, EMPTY_ARRAY)
}

var isSameAction = function(a, b) {
  return isArray(a) && isArray(b) && a[0] === b[0] && typeof a[0] === "function"
}

var shouldRestart = function(a, b) {
  for (var k in merge(a, b)) {
    if (a[k] === b[k] || isSameAction(a[k], b[k])) b[k] = a[k]
    else return true
  }
}

var patchSub = function(sub, newSub, dispatch) {
  for (var i = 0, a, b, out = []; i < sub.length || i < newSub.length; i++) {
    a = sub[i]
    out.push(
      (b = newSub[i])
        ? !a || b[0] !== a[0] || shouldRestart(b[1], a[1])
          ? [b[0], b[1], b[0](b[1], dispatch), a && a[2]()]
          : a
        : a && a[2]()
    )
  }
  return out
}

var updateProperty = function(element, name, value, newValue, eventCb, isSvg) {
  if (name === "key") {
  } else if (name === "style") {
    for (var i in merge(value, newValue)) {
      var style = newValue == null || newValue[i] == null ? "" : newValue[i]
      if (i[0] === "-") {
        element[name].setProperty(i, style)
      } else {
        element[name][i] = style
      }
    }
  } else if (name[0] === "o" && name[1] === "n") {
    if (
      !((element.events || (element.events = {}))[
        (name = name.slice(2))
      ] = newValue)
    ) {
      element.removeEventListener(name, eventCb)
    } else if (!value) {
      element.addEventListener(name, eventCb)
    }
  } else if (name !== "list" && !isSvg && name in element) {
    element[name] = newValue == null ? "" : newValue
  } else if (
    newValue == null ||
    newValue === false ||
    (name === "class" && !(newValue = createClass(newValue)))
  ) {
    element.removeAttribute(name)
  } else {
    element.setAttribute(name, newValue)
  }
}

var removeElement = function(parent, node) {
  parent.removeChild(node.element)
}

var createElement = function(node, eventCb, isSvg) {
  var element =
    node.type === TEXT_NODE
      ? document.createTextNode(node.name)
      : (isSvg = isSvg || node.name === "svg")
      ? document.createElementNS("http://www.w3.org/2000/svg", node.name)
      : document.createElement(node.name)
  var props = node.props

  for (var k in props) {
    updateProperty(element, k, null, props[k], eventCb, isSvg)
  }

  for (var i = 0, len = node.children.length; i < len; i++) {
    element.appendChild(
      createElement(
        (node.children[i] = getNode(node.children[i])),
        eventCb,
        isSvg
      )
    )
  }

  return (node.element = element)
}

var updateElement = function(element, props, newProps, eventCb, isSvg) {
  for (var k in merge(props, newProps)) {
    if (
      (k === "value" || k === "selected" || k === "checked"
        ? element[k]
        : props[k]) !== newProps[k]
    ) {
      updateProperty(element, k, props[k], newProps[k], eventCb, isSvg)
    }
  }
}

var getKey = function(node) {
  return node == null ? null : node.key
}

var patch = function(parent, element, node, newNode, eventCb, isSvg) {
  if (newNode === node) {
  } else if (
    node != null &&
    node.type === TEXT_NODE &&
    newNode.type === TEXT_NODE
  ) {
    if (node.name !== newNode.name) element.nodeValue = newNode.name
  } else if (node == null || node.name !== newNode.name) {
    var newElement = parent.insertBefore(
      createElement((newNode = getNode(newNode)), eventCb, isSvg),
      element
    )

    if (node != null) removeElement(parent, node)

    element = newElement
  } else {
    updateElement(
      element,
      node.props,
      newNode.props,
      eventCb,
      (isSvg = isSvg || newNode.name === "svg")
    )

    var savedNode
    var childNode

    var key
    var children = node.children
    var start = 0
    var end = children.length - 1

    var newKey
    var newChildren = newNode.children
    var newStart = 0
    var newEnd = newChildren.length - 1

    while (newStart <= newEnd && start <= end) {
      key = getKey(children[start])
      newKey = getKey(newChildren[newStart])

      if (key == null || key !== newKey) break

      patch(
        element,
        children[start].element,
        children[start],
        (newChildren[newStart] = getNode(
          newChildren[newStart],
          children[start]
        )),
        eventCb,
        isSvg
      )

      start++
      newStart++
    }

    while (newStart <= newEnd && start <= end) {
      key = getKey(children[end])
      newKey = getKey(newChildren[newEnd])

      if (key == null || key !== newKey) break

      patch(
        element,
        children[end].element,
        children[end],
        (newChildren[newEnd] = getNode(newChildren[newEnd], children[end])),
        eventCb,
        isSvg
      )

      end--
      newEnd--
    }

    if (start > end) {
      while (newStart <= newEnd) {
        element.insertBefore(
          createElement(
            (newChildren[newStart] = getNode(newChildren[newStart++])),
            eventCb,
            isSvg
          ),
          (childNode = children[start]) && childNode.element
        )
      }
    } else if (newStart > newEnd) {
      while (start <= end) {
        removeElement(element, children[start++])
      }
    } else {
      for (var i = start, keyed = {}, newKeyed = {}; i <= end; i++) {
        if ((key = children[i].key) != null) {
          keyed[key] = children[i]
        }
      }

      while (newStart <= newEnd) {
        key = getKey((childNode = children[start]))
        newKey = getKey(
          (newChildren[newStart] = getNode(newChildren[newStart], childNode))
        )

        if (
          newKeyed[key] ||
          (newKey != null && newKey === getKey(children[start + 1]))
        ) {
          if (key == null) {
            removeElement(element, childNode)
          }
          start++
          continue
        }

        if (newKey == null || node.type === RECYCLED_NODE) {
          if (key == null) {
            patch(
              element,
              childNode && childNode.element,
              childNode,
              newChildren[newStart],
              eventCb,
              isSvg
            )
            newStart++
          }
          start++
        } else {
          if (key === newKey) {
            patch(
              element,
              childNode.element,
              childNode,
              newChildren[newStart],
              eventCb,
              isSvg
            )
            newKeyed[newKey] = true
            start++
          } else {
            if ((savedNode = keyed[newKey]) != null) {
              patch(
                element,
                element.insertBefore(
                  savedNode.element,
                  childNode && childNode.element
                ),
                savedNode,
                newChildren[newStart],
                eventCb,
                isSvg
              )
              newKeyed[newKey] = true
            } else {
              patch(
                element,
                childNode && childNode.element,
                null,
                newChildren[newStart],
                eventCb,
                isSvg
              )
            }
          }
          newStart++
        }
      }

      while (start <= end) {
        if (getKey((childNode = children[start++])) == null) {
          removeElement(element, childNode)
        }
      }

      for (var key in keyed) {
        if (newKeyed[key] == null) {
          removeElement(element, keyed[key])
        }
      }
    }
  }

  return (newNode.element = element)
}

var shouldUpdate = function(a, b) {
  for (var k in a) if (a[k] !== b[k]) return true
  for (var k in b) if (a[k] !== b[k]) return true
}

var getNode = function(newNode, node) {
  return newNode.type === LAZY_NODE
    ? !node || shouldUpdate(newNode.lazy, node.lazy)
      ? newNode.render()
      : node
    : newNode
}

var createVNode = function(name, props, children, element, key, type) {
  return {
    name: name,
    props: props,
    children: children,
    element: element,
    type: type,
    key: key
  }
}

var createTextVNode = function(text, element) {
  return createVNode(text, EMPTY_OBJECT, EMPTY_ARRAY, element, null, TEXT_NODE)
}

var recycleElement = function(element) {
  return element.nodeType === TEXT_NODE
    ? createTextVNode(element.nodeValue, element)
    : createVNode(
        element.nodeName.toLowerCase(),
        EMPTY_OBJECT,
        map.call(element.childNodes, recycleElement),
        element,
        null,
        RECYCLED_NODE
      )
}

export var Lazy = function(props) {
  return {
    type: LAZY_NODE,
    key: props.key,
    lazy: props,
    render: function() {
      var node = props.render(props)
      node.lazy = props
      return node
    }
  }
}

export var h = function(name, props) {
  for (var node, rest = [], children = [], i = arguments.length; i-- > 2; ) {
    rest.push(arguments[i])
  }

  while (rest.length > 0) {
    if (isArray((node = rest.pop()))) {
      for (i = node.length; i-- > 0; ) rest.push(node[i])
    } else if (node === false || node === true || node == null) {
    } else {
      children.push(typeof node === "object" ? node : createTextVNode(node))
    }
  }

  props = props || EMPTY_OBJECT

  return typeof name === "function"
    ? name(props, children)
    : createVNode(name, props, children, null, props.key, DEFAULT_NODE)
}

export var app = function(props, decorateDispatch) {
  var container = props.container
  var element = container && container.children[0]
  var node = element && recycleElement(element)
  var subs = props.subscriptions
  var view = props.view
  var lock = false
  var state = {}
  var sub = []
  var decorator = decorateDispatch || (function (d) { return d })

  var eventCb = function(event) {
    dispatch(event.currentTarget.events[event.type], event)
  }

  var setState = function(newState) {
    if (!(state === newState || lock)) defer(render, (lock = true))
    state = newState
  }

  var dispatch = decorator(function(obj, props) {
    if (typeof obj === "function") {
      dispatch(obj(state, props))
    } else if (isArray(obj)) {
      if (typeof obj[0] === "function") {
        dispatch(obj[0](state, obj[1], props))
      } else {
        flatten(obj.slice(1)).map(function(fx) {
          fx && fx[0](fx[1], dispatch)
        }, setState(obj[0]))
      }
    } else {
      setState(obj)
    }
  })

  var render = function() {
    lock = false
    if (subs) sub = patchSub(sub, flatten(subs(state)), dispatch)
    if (view) {
      element = patch(container, element, node, (node = view(state)), eventCb)
    }
  }

  dispatch(props.init)
}
<|endoftext|>


<commit_before>// @flow
import EventSource, { sources } from './EventSource';

export default EventSource;
export { sources };
<commit_msg>style: apply format<commit_after>// @flow
import EventSource, { sources } from "./EventSource";

export default EventSource;
export { sources };
<|endoftext|>


<commit_before>// @flow

import 'babel-polyfill';
import express from 'express';
import type { $Request } from 'express';

import compression from 'compression';
import favicon from 'serve-favicon';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { createStore, applyMiddleware } from 'redux';
import type { Store } from 'redux';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';

import batcher from './redux-batcher';
import type { BatchedAction } from './redux-batcher';

import type { State } from './types/state';
import layout from './layout';
import App from './components/app';
import reducer from './reducer';
import init from './init';
import translate from './router';
import { measurablesByKey } from './measurables';
import { positions } from './positions';
import serverApi from './api/server';
import bundles from './bundles.json';
import { HttpRedirect } from './http';

init().then((stores) => {
  const app = express();

  const api = serverApi(stores);

  app.set('x-powered-by', false);
  app.set('port', (process.env.PORT || 5000));
  app.use(compression());
  app.use('/public', express.static(`${__dirname}/../public`, {
    maxAge: 1000 * 60 * 60 * 24 * 365, // one year
  }));
  app.use(favicon(`${__dirname}/../public/favicon.ico`));
  app.get('/api/search', async (req: $Request, res) => {
    res.send(JSON.stringify(await api.fetchSearchResults(
      JSON.parse(req.query.opts),
      req.query.pos,
    )));
  });
  app.get('/api/player', async (req: $Request, res) => {
    res.send(JSON.stringify(await api.fetchPlayer(req.query.id)));
  });
  app.get('/api/comparisons', async (req: $Request, res) => {
    res.send(JSON.stringify(await api.fetchComparisons(req.query.id, req.query.pos)));
  });
  app.get('/api/percentiles', async (req: $Request, res) => {
    res.send(JSON.stringify(await api.fetchPercentiles(req.query.id, req.query.pos)));
  });
  app.get('/api/typeahead', async (req: $Request, res) => {
    res.send(JSON.stringify(await api.fetchTypeAheadResults(req.query.search)));
  });

  app.get('/.well-known/acme-challenge/XH4Lt6PqOCVWwifNvHuR90st0fyShGjWaBDDwoUjrJc', (req: $Request, res) => {
    res.send('XH4Lt6PqOCVWwifNvHuR90st0fyShGjWaBDDwoUjrJc.i8Z5Sr5IA3VD1phPjT0oWE2bVrnLwpaGPE9KjcK9M_I');
  });

  const validPositions = positions.filter(pos => !!stores.positionEligibilityStore.get(pos.id));
  const posById = validPositions.reduce((a, pos) => Object.assign({}, a, { [pos.id]: pos }), {});

  app.get('*', async (req: $Request, res) => {
    const store: Store<State, BatchedAction> = createStore(reducer, {
      measurables: measurablesByKey,
      positions: posById,
      comparisons: {},
      percentiles: {},
      players: {},
      selectedPositionId: 'ATH',
      modalType: 'None',
      embed: false,
    }, applyMiddleware(batcher, thunk.withExtraArgument(api)));
    try {
      await store.dispatch(await translate(req.path, req.query));
    } catch (e) {
      if (e instanceof HttpRedirect) {
        res.redirect(e.code, e.location);
        return;
      }
    }
    const jsBundleName = bundles.js_bundle_name || 'public/bundle.js';
    const cssBundleName = bundles.css_bundle_name || 'public/bundle.css';
    res.send(
      layout(
        'MockDraftable',
        renderToString(<Provider store={store}><App /></Provider>),
        store.getState(),
        jsBundleName,
        cssBundleName,
      ),
    );
  });

  const port = app.get('port').toString();
  app.listen(port);
  console.log(`MockDraftable now listening on ${port}`);
}).catch((e) => { console.log(e.stack); });
<commit_msg>Trying again<commit_after>// @flow

import 'babel-polyfill';
import express from 'express';
import type { $Request } from 'express';

import compression from 'compression';
import favicon from 'serve-favicon';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { createStore, applyMiddleware } from 'redux';
import type { Store } from 'redux';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';

import batcher from './redux-batcher';
import type { BatchedAction } from './redux-batcher';

import type { State } from './types/state';
import layout from './layout';
import App from './components/app';
import reducer from './reducer';
import init from './init';
import translate from './router';
import { measurablesByKey } from './measurables';
import { positions } from './positions';
import serverApi from './api/server';
import bundles from './bundles.json';
import { HttpRedirect } from './http';

init().then((stores) => {
  const app = express();

  const api = serverApi(stores);

  app.set('x-powered-by', false);
  app.set('port', (process.env.PORT || 5000));
  app.use(compression());
  app.use('/public', express.static(`${__dirname}/../public`, {
    maxAge: 1000 * 60 * 60 * 24 * 365, // one year
  }));
  app.use(favicon(`${__dirname}/../public/favicon.ico`));
  app.get('/api/search', async (req: $Request, res) => {
    res.send(JSON.stringify(await api.fetchSearchResults(
      JSON.parse(req.query.opts),
      req.query.pos,
    )));
  });
  app.get('/api/player', async (req: $Request, res) => {
    res.send(JSON.stringify(await api.fetchPlayer(req.query.id)));
  });
  app.get('/api/comparisons', async (req: $Request, res) => {
    res.send(JSON.stringify(await api.fetchComparisons(req.query.id, req.query.pos)));
  });
  app.get('/api/percentiles', async (req: $Request, res) => {
    res.send(JSON.stringify(await api.fetchPercentiles(req.query.id, req.query.pos)));
  });
  app.get('/api/typeahead', async (req: $Request, res) => {
    res.send(JSON.stringify(await api.fetchTypeAheadResults(req.query.search)));
  });

  app.get('/.well-known/acme-challenge/sJI2fruuWtN3goKIyUSp25S4F7O8dbs0d7baPjPkSEM', (req: $Request, res) => {
    res.send('sJI2fruuWtN3goKIyUSp25S4F7O8dbs0d7baPjPkSEM.i8Z5Sr5IA3VD1phPjT0oWE2bVrnLwpaGPE9KjcK9M_I');
  });

  const validPositions = positions.filter(pos => !!stores.positionEligibilityStore.get(pos.id));
  const posById = validPositions.reduce((a, pos) => Object.assign({}, a, { [pos.id]: pos }), {});

  app.get('*', async (req: $Request, res) => {
    const store: Store<State, BatchedAction> = createStore(reducer, {
      measurables: measurablesByKey,
      positions: posById,
      comparisons: {},
      percentiles: {},
      players: {},
      selectedPositionId: 'ATH',
      modalType: 'None',
      embed: false,
    }, applyMiddleware(batcher, thunk.withExtraArgument(api)));
    try {
      await store.dispatch(await translate(req.path, req.query));
    } catch (e) {
      if (e instanceof HttpRedirect) {
        res.redirect(e.code, e.location);
        return;
      }
    }
    const jsBundleName = bundles.js_bundle_name || 'public/bundle.js';
    const cssBundleName = bundles.css_bundle_name || 'public/bundle.css';
    res.send(
      layout(
        'MockDraftable',
        renderToString(<Provider store={store}><App /></Provider>),
        store.getState(),
        jsBundleName,
        cssBundleName,
      ),
    );
  });

  const port = app.get('port').toString();
  app.listen(port);
  console.log(`MockDraftable now listening on ${port}`);
}).catch((e) => { console.log(e.stack); });
<|endoftext|>


<commit_before>'use strict'

/**
 * @module serverless-plugin-warmup
 *
 * @see {@link https://serverless.com/framework/docs/providers/aws/guide/plugins/}
 *
 * @requires 'bluebird'
 * @requires 'fs-extra'
 * @requires 'path'
 * */
const BbPromise = require('bluebird')
const fs = BbPromise.promisifyAll(require('fs-extra'))
const path = require('path')

/**
 * @classdesc Keep your lambdas warm during Winter
 * @class WarmUP
 * */
class WarmUP {
  /**
   * @description Serverless Warm Up
   * @constructor
   *
   * @param {!Object} serverless - Serverless object
   * @param {!Object} options - Serverless options
   * */
  constructor (serverless, options) {
    /** Serverless variables */
    this.serverless = serverless
    this.options = options
    this.custom = this.serverless.service.custom

    this.provider = this.serverless.getProvider('aws')

    this.hooks = {
      'after:package:initialize': this.afterPackageInitialize.bind(this),
      'after:package:createDeploymentArtifacts': this.afterCreateDeploymentArtifacts.bind(this),
      'after:deploy:deploy': this.afterDeployFunctions.bind(this)
    }
  }

  /**
   * @description After package initialize hook
   *
   * @fulfil {} — Warm up set
   * @reject {Error} Warm up error
   *
   * @return {(boolean|Promise)}
   * */
  afterPackageInitialize () {
    this.configPlugin()

    /** Create warmer function and add it to the service */
    return this.createWarmer()
  }

  /**
   * @description After create deployment artifacts
   *
   * @fulfil {} — Optimization finished
   * @reject {Error} Optimization error
   *
   * @return {Promise}
   * */
  afterCreateDeploymentArtifacts () {
    /** Clean prefix folder */
    return this.cleanFolder()
  }

  /**
   * @description After deploy functions hooks
   *
   * @fulfil {} — Functions warmed up sucessfuly
   * @reject {Error} Functions couldn't be warmed up
   *
   * @return {Promise}
   * */
  afterDeployFunctions () {
    if (this.warmup.prewarm) {
      return this.warmUpFunctions()
    }
  }

  /**
   * @description Configure the plugin based on the context of serverless.yml
   *
   * @return {}
   * */
  configPlugin () {
    /** Set warm up folder, file and handler paths */
    this.folderName = '_warmup'
    this.pathFolder = this.getPath(this.folderName)
    this.pathFile = this.pathFolder + '/index.js'
    this.pathHandler = this.folderName + '/index.warmUp'

    /** Default options */
    this.warmup = {
      memorySize: 128,
      name: 'warmup-plugin-' + this.serverless.service.service + '-' + this.options.stage,
      schedule: 'rate(5 minutes)',
      timeout: 10,
      prewarm: false
    }

    /** Set global custom options */
    if (!this.custom || !this.custom.warmup) {
      return
    }

    /** Memory size */
    if (typeof this.custom.warmup.memorySize === 'number') {
      this.warmup.memorySize = this.custom.warmup.memorySize
    }

    /** Function name */
    if (typeof this.custom.warmup.name === 'string') {
      this.warmup.name = this.custom.warmup.name
    }

    /** Schedule expression */
    if (typeof this.custom.warmup.schedule === 'string') {
      this.warmup.schedule = this.custom.warmup.schedule
    }

    /** Timeout */
    if (typeof this.custom.warmup.timeout === 'number') {
      this.warmup.timeout = this.custom.warmup.timeout
    }

    /** Pre-warm */
    if (typeof this.custom.warmup.prewarm === 'boolean') {
      this.warmup.prewarm = this.custom.warmup.prewarm
    }
  }

  /**
   * @description After create deployment artifacts
   *
   * @param {string} file — File path
   *
   * @return {String} Absolute file path
   * */
  getPath (file) {
    return path.join(this.serverless.config.servicePath, file)
  }

  /**
   * @description Clean prefix folder
   *
   * @fulfil {} — Folder cleaned
   * @reject {Error} File system error
   *
   * @return {Promise}
   * */
  cleanFolder () {
    return fs.removeAsync(this.pathFolder)
  }

  /**
   * @description Warm up functions
   *
   * @fulfil {} — Warm up function created and added to service
   * @reject {Error} Warm up error
   *
   * @return {Promise}
   * */
  createWarmer () {
    /** Get functions */
    const allFunctions = this.serverless.service.getAllFunctions()

    /** Filter functions for warm up */
    return BbPromise.filter(allFunctions, (functionName) => {
      const functionObject = this.serverless.service.getFunction(functionName)

      /** Function needs to be warm */
      if (functionObject.warmup === true) {
        return functionObject
      }
    }).then((functionNames) => {
      /** Skip writing if no functions need to be warm */
      if (!functionNames.length) {
        /** Log no warmup */
        this.serverless.cli.log('WarmUP: no lambda to warm')
        return true
      }

      /** Write warm up function */
      return this.createWarmUpFunctionArtifact(functionNames)
    }).then((skip) => {
      /** Add warm up function to service */
      if (skip !== true) {
        return this.addWarmUpFunctionToService()
      }
    })
  }

  /**
   * @description Write warm up ES6 function
   *
   * @param {Array} functionNames - Function names
   *
   * @fulfil {} — Warm up function created
   * @reject {Error} Warm up error
   *
   * @return {Promise}
   * */
  createWarmUpFunctionArtifact (functionNames) {
    /** Log warmup start */
    this.serverless.cli.log('WarmUP: setting ' + functionNames.length + ' lambdas to be warm')

    /** Get function names */
    functionNames = functionNames.map((functionName) => {
      const functionObject = this.serverless.service.getFunction(functionName)
      this.serverless.cli.log('WarmUP: ' + functionObject.name)
      return functionObject.name
    })

    /** Write function invoke promises and push to array */
    const warmUpFunction = '"use strict";\n\n' +
      '/** Generated by Serverless WarmUP Plugin at ' + new Date().toISOString() + ' */\n' +
      'const aws = require("aws-sdk");\n' +
      'aws.config.region = "' + this.serverless.service.provider.region + '";\n' +
      'const lambda = new aws.Lambda();\n' +
      'const functionNames = "' + functionNames.join() + '".split(",");\n' +
      'module.exports.warmUp = (event, context, callback) => {\n' +
      '  let invokes = [];\n' +
      '  let errors = 0;\n' +
      '  console.log("Warm Up Start");\n' +
      '  functionNames.forEach((functionName) => {\n' +
      '    const params = {\n' +
      '      FunctionName: functionName,\n' +
      '      InvocationType: "RequestResponse",\n' +
      '      LogType: "None",\n' +
      '      Qualifier: process.env.SERVERLESS_ALIAS || "$LATEST",\n' +
      '      Payload: JSON.stringify({ source: "serverless-plugin-warmup" })\n' +
      '    };\n' +
      '    invokes.push(lambda.invoke(params).promise().then((data) => {\n' +
      '      console.log("Warm Up Invoke Success: " + functionName + "", data);\n' +
      '    }, (error) => {\n' +
      '      errors++;\n' +
      '      console.log("Warm Up Invoke Error: " + functionName + "", error);\n' +
      '    }));\n' +
      '  });\n' +
      '  Promise.all(invokes).then(() => {\n' +
      '    console.log("Warm Up Finished with " + errors + " invoke errors");\n' +
      '    callback();\n' +
      '  });\n' +
      '}'

    /** Write warm up file */
    return fs.outputFileAsync(this.pathFile, warmUpFunction)
  }

  /**
   * @description Add warm up function to service
   *
   * @return {Object} Warm up service function object
   * */
  addWarmUpFunctionToService () {
    /** SLS warm up function */
    this.serverless.service.functions.warmUpPlugin = {
      description: 'Serverless WarmUP Plugin',
      events: [
        {
          schedule: this.warmup.schedule
        }
      ],
      handler: this.pathHandler,
      memorySize: this.warmup.memorySize,
      name: this.warmup.name,
      runtime: 'nodejs6.10',
      package: {
        exclude: ['**'],
        include: [this.folderName + '/**']
      },
      timeout: this.warmup.timeout
    }

    /** Return service function object */
    return this.serverless.service.functions.warmUpPlugin
  }

  /**
   * @description Warm up the functions immediately after deployment
   *
   * @fulfil {} — Functions warmed up sucessfuly
   * @reject {Error} Functions couldn't be warmed up
   *
   * @return {Promise}
   * */
  warmUpFunctions () {
    this.serverless.cli.log('WarmUP: Pre-warming up your functions')

    const params = {
      FunctionName: this.warmup.name,
      InvocationType: 'RequestResponse',
      LogType: 'None',
      Qualifier: process.env.SERVERLESS_ALIAS || '$LATEST',
      Payload: JSON.stringify({ source: 'serverless-plugin-warmup' })
    }

    return this.provider.request('Lambda', 'invoke', params)
      .then(data => this.serverless.cli.log('WarmUp: Functions sucessfuly pre-warmed'))
      .catch(error => this.serverless.cli.log('WarmUp: Error while pre-warming functions', error))
  }
}

/** Export WarmUP class */
module.exports = WarmUP
<commit_msg>feature (warmer function name): Rename warmer lambda function to `service-stage-warmup-plugin` so it's consistent with the rest of function names generated by serverless<commit_after>'use strict'

/**
 * @module serverless-plugin-warmup
 *
 * @see {@link https://serverless.com/framework/docs/providers/aws/guide/plugins/}
 *
 * @requires 'bluebird'
 * @requires 'fs-extra'
 * @requires 'path'
 * */
const BbPromise = require('bluebird')
const fs = BbPromise.promisifyAll(require('fs-extra'))
const path = require('path')

/**
 * @classdesc Keep your lambdas warm during Winter
 * @class WarmUP
 * */
class WarmUP {
  /**
   * @description Serverless Warm Up
   * @constructor
   *
   * @param {!Object} serverless - Serverless object
   * @param {!Object} options - Serverless options
   * */
  constructor (serverless, options) {
    /** Serverless variables */
    this.serverless = serverless
    this.options = options
    this.custom = this.serverless.service.custom

    this.provider = this.serverless.getProvider('aws')

    this.hooks = {
      'after:package:initialize': this.afterPackageInitialize.bind(this),
      'after:package:createDeploymentArtifacts': this.afterCreateDeploymentArtifacts.bind(this),
      'after:deploy:deploy': this.afterDeployFunctions.bind(this)
    }
  }

  /**
   * @description After package initialize hook
   *
   * @fulfil {} — Warm up set
   * @reject {Error} Warm up error
   *
   * @return {(boolean|Promise)}
   * */
  afterPackageInitialize () {
    this.configPlugin()

    /** Create warmer function and add it to the service */
    return this.createWarmer()
  }

  /**
   * @description After create deployment artifacts
   *
   * @fulfil {} — Optimization finished
   * @reject {Error} Optimization error
   *
   * @return {Promise}
   * */
  afterCreateDeploymentArtifacts () {
    /** Clean prefix folder */
    return this.cleanFolder()
  }

  /**
   * @description After deploy functions hooks
   *
   * @fulfil {} — Functions warmed up sucessfuly
   * @reject {Error} Functions couldn't be warmed up
   *
   * @return {Promise}
   * */
  afterDeployFunctions () {
    if (this.warmup.prewarm) {
      return this.warmUpFunctions()
    }
  }

  /**
   * @description Configure the plugin based on the context of serverless.yml
   *
   * @return {}
   * */
  configPlugin () {
    /** Set warm up folder, file and handler paths */
    this.folderName = '_warmup'
    this.pathFolder = this.getPath(this.folderName)
    this.pathFile = this.pathFolder + '/index.js'
    this.pathHandler = this.folderName + '/index.warmUp'

    /** Default options */
    this.warmup = {
      memorySize: 128,
      name: this.serverless.service.service + '-' + this.options.stage + 'warmup-plugin',
      schedule: 'rate(5 minutes)',
      timeout: 10,
      prewarm: false
    }

    /** Set global custom options */
    if (!this.custom || !this.custom.warmup) {
      return
    }

    /** Memory size */
    if (typeof this.custom.warmup.memorySize === 'number') {
      this.warmup.memorySize = this.custom.warmup.memorySize
    }

    /** Function name */
    if (typeof this.custom.warmup.name === 'string') {
      this.warmup.name = this.custom.warmup.name
    }

    /** Schedule expression */
    if (typeof this.custom.warmup.schedule === 'string') {
      this.warmup.schedule = this.custom.warmup.schedule
    }

    /** Timeout */
    if (typeof this.custom.warmup.timeout === 'number') {
      this.warmup.timeout = this.custom.warmup.timeout
    }

    /** Pre-warm */
    if (typeof this.custom.warmup.prewarm === 'boolean') {
      this.warmup.prewarm = this.custom.warmup.prewarm
    }
  }

  /**
   * @description After create deployment artifacts
   *
   * @param {string} file — File path
   *
   * @return {String} Absolute file path
   * */
  getPath (file) {
    return path.join(this.serverless.config.servicePath, file)
  }

  /**
   * @description Clean prefix folder
   *
   * @fulfil {} — Folder cleaned
   * @reject {Error} File system error
   *
   * @return {Promise}
   * */
  cleanFolder () {
    return fs.removeAsync(this.pathFolder)
  }

  /**
   * @description Warm up functions
   *
   * @fulfil {} — Warm up function created and added to service
   * @reject {Error} Warm up error
   *
   * @return {Promise}
   * */
  createWarmer () {
    /** Get functions */
    const allFunctions = this.serverless.service.getAllFunctions()

    /** Filter functions for warm up */
    return BbPromise.filter(allFunctions, (functionName) => {
      const functionObject = this.serverless.service.getFunction(functionName)

      /** Function needs to be warm */
      if (functionObject.warmup === true) {
        return functionObject
      }
    }).then((functionNames) => {
      /** Skip writing if no functions need to be warm */
      if (!functionNames.length) {
        /** Log no warmup */
        this.serverless.cli.log('WarmUP: no lambda to warm')
        return true
      }

      /** Write warm up function */
      return this.createWarmUpFunctionArtifact(functionNames)
    }).then((skip) => {
      /** Add warm up function to service */
      if (skip !== true) {
        return this.addWarmUpFunctionToService()
      }
    })
  }

  /**
   * @description Write warm up ES6 function
   *
   * @param {Array} functionNames - Function names
   *
   * @fulfil {} — Warm up function created
   * @reject {Error} Warm up error
   *
   * @return {Promise}
   * */
  createWarmUpFunctionArtifact (functionNames) {
    /** Log warmup start */
    this.serverless.cli.log('WarmUP: setting ' + functionNames.length + ' lambdas to be warm')

    /** Get function names */
    functionNames = functionNames.map((functionName) => {
      const functionObject = this.serverless.service.getFunction(functionName)
      this.serverless.cli.log('WarmUP: ' + functionObject.name)
      return functionObject.name
    })

    /** Write function invoke promises and push to array */
    const warmUpFunction = '"use strict";\n\n' +
      '/** Generated by Serverless WarmUP Plugin at ' + new Date().toISOString() + ' */\n' +
      'const aws = require("aws-sdk");\n' +
      'aws.config.region = "' + this.serverless.service.provider.region + '";\n' +
      'const lambda = new aws.Lambda();\n' +
      'const functionNames = "' + functionNames.join() + '".split(",");\n' +
      'module.exports.warmUp = (event, context, callback) => {\n' +
      '  let invokes = [];\n' +
      '  let errors = 0;\n' +
      '  console.log("Warm Up Start");\n' +
      '  functionNames.forEach((functionName) => {\n' +
      '    const params = {\n' +
      '      FunctionName: functionName,\n' +
      '      InvocationType: "RequestResponse",\n' +
      '      LogType: "None",\n' +
      '      Qualifier: process.env.SERVERLESS_ALIAS || "$LATEST",\n' +
      '      Payload: JSON.stringify({ source: "serverless-plugin-warmup" })\n' +
      '    };\n' +
      '    invokes.push(lambda.invoke(params).promise().then((data) => {\n' +
      '      console.log("Warm Up Invoke Success: " + functionName + "", data);\n' +
      '    }, (error) => {\n' +
      '      errors++;\n' +
      '      console.log("Warm Up Invoke Error: " + functionName + "", error);\n' +
      '    }));\n' +
      '  });\n' +
      '  Promise.all(invokes).then(() => {\n' +
      '    console.log("Warm Up Finished with " + errors + " invoke errors");\n' +
      '    callback();\n' +
      '  });\n' +
      '}'

    /** Write warm up file */
    return fs.outputFileAsync(this.pathFile, warmUpFunction)
  }

  /**
   * @description Add warm up function to service
   *
   * @return {Object} Warm up service function object
   * */
  addWarmUpFunctionToService () {
    /** SLS warm up function */
    this.serverless.service.functions.warmUpPlugin = {
      description: 'Serverless WarmUP Plugin',
      events: [
        {
          schedule: this.warmup.schedule
        }
      ],
      handler: this.pathHandler,
      memorySize: this.warmup.memorySize,
      name: this.warmup.name,
      runtime: 'nodejs6.10',
      package: {
        exclude: ['**'],
        include: [this.folderName + '/**']
      },
      timeout: this.warmup.timeout
    }

    /** Return service function object */
    return this.serverless.service.functions.warmUpPlugin
  }

  /**
   * @description Warm up the functions immediately after deployment
   *
   * @fulfil {} — Functions warmed up sucessfuly
   * @reject {Error} Functions couldn't be warmed up
   *
   * @return {Promise}
   * */
  warmUpFunctions () {
    this.serverless.cli.log('WarmUP: Pre-warming up your functions')

    const params = {
      FunctionName: this.warmup.name,
      InvocationType: 'RequestResponse',
      LogType: 'None',
      Qualifier: process.env.SERVERLESS_ALIAS || '$LATEST',
      Payload: JSON.stringify({ source: 'serverless-plugin-warmup' })
    }

    return this.provider.request('Lambda', 'invoke', params)
      .then(data => this.serverless.cli.log('WarmUp: Functions sucessfuly pre-warmed'))
      .catch(error => this.serverless.cli.log('WarmUp: Error while pre-warming functions', error))
  }
}

/** Export WarmUP class */
module.exports = WarmUP
<|endoftext|>


<commit_before>import assert from 'assert'
import path from 'path'
import fs from 'fs'
import pathExists from 'path-exists'
import merge from 'lodash.merge'
import invariant from 'invariant'
import * as babelCore from 'babel-core'
import stripIndent from 'strip-indent'
import {oneLine} from 'common-tags'

const noop = () => {}

module.exports = pluginTester

const fullDefaultConfig = {
  babelOptions: {
    parserOpts: {},
    generatorOpts: {},
    babelrc: false,
  },
}

function pluginTester({
  /* istanbul ignore next (TODO: write a test for this) */
  babel = babelCore,
  plugin = requiredParam('plugin'),
  pluginName = getPluginName(plugin, babel),
  title: describeBlockTitle = pluginName,
  pluginOptions,
  tests,
  fixtures,
  fixtureOutputName = 'output',
  filename,
  ...rest
} = {}) {
  let testNumber = 1
  if (fixtures) {
    testFixtures({
      plugin,
      pluginName,
      pluginOptions,
      title: describeBlockTitle,
      fixtures,
      fixtureOutputName,
      filename,
      babel,
      ...rest,
    })
  }
  const testAsArray = toTestArray(tests)
  if (!testAsArray.length) {
    return Promise.resolve()
  }
  const testerConfig = merge({}, fullDefaultConfig, rest)

  return describe(describeBlockTitle, () => {
    const promises = testAsArray.map(testConfig => {
      if (!testConfig) {
        return Promise.resolve()
      }
      const {
        skip,
        only,
        title,
        code,
        babelOptions,
        output,
        snapshot,
        error,
        setup = noop,
        teardown,
        formatResult = r => r,
      } = merge({}, testerConfig, toTestConfig(testConfig))
      assert(
        (!skip && !only) || skip !== only,
        'Cannot enable both skip and only on a test',
      )

      if (skip) {
        // eslint-disable-next-line jest/no-disabled-tests
        return it.skip(title, testerWrapper)
      } else if (only) {
        // eslint-disable-next-line jest/no-focused-tests
        return it.only(title, testerWrapper)
      } else {
        return it(title, testerWrapper)
      }

      async function testerWrapper() {
        const teardowns = teardown ? [teardown] : []
        let returnedTeardown
        try {
          returnedTeardown = await setup()
        } catch (e) {
          // eslint-disable-next-line no-console
          console.error('There was a problem during setup')
          throw e
        }
        if (typeof returnedTeardown === 'function') {
          teardowns.push(returnedTeardown)
        }
        try {
          tester()
        } finally {
          try {
            await Promise.all(teardowns.map(t => t()))
          } catch (e) {
            // eslint-disable-next-line no-console
            console.error('There was a problem during teardown')
            // eslint-disable-next-line no-unsafe-finally
            throw e
          }
        }
      }

      // eslint-disable-next-line complexity
      function tester() {
        invariant(
          code,
          oneLine`
            A string or object with a \`code\` or
            \`fixture\` property must be provided
          `,
        )
        invariant(
          !babelOptions.babelrc || babelOptions.filename,
          'babelrc set to true, but no filename specified in babelOptions',
        )
        invariant(
          !snapshot || !output,
          '`output` cannot be provided with `snapshot: true`',
        )

        let result
        let errored = false

        try {
          result = formatResult(babel.transform(code, babelOptions).code.trim())
        } catch (err) {
          if (error) {
            errored = true
            result = err
          } else {
            throw err
          }
        }

        const expectedToThrowButDidNot = error && !errored
        assert(
          !expectedToThrowButDidNot,
          'Expected to throw error, but it did not.',
        )

        if (snapshot) {
          invariant(
            result !== code,
            oneLine`
              Code was unmodified but attempted to take a snapshot.
              If the code should not be modified, set \`snapshot: false\`
            `,
          )
          const separator = '\n\n      ↓ ↓ ↓ ↓ ↓ ↓\n\n'
          const formattedOutput = [code, separator, result].join('')
          expect(`\n${formattedOutput}\n`).toMatchSnapshot(title)
        } else if (error) {
          assertError(result, error)
        } else if (output) {
          assert.equal(result, output, 'Output is incorrect.')
        } else {
          assert.equal(
            result,
            code,
            'Expected output to not change, but it did',
          )
        }
      }
    })

    return Promise.all(promises)
  })

  function toTestConfig(testConfig) {
    if (typeof testConfig === 'string') {
      testConfig = {code: testConfig}
    }
    const {
      title,
      fixture,
      code = getCode(filename, fixture),
      fullTitle = title || `${testNumber++}. ${pluginName}`,
      output = getCode(filename, testConfig.outputFixture),
      pluginOptions: testOptions = pluginOptions,
    } = testConfig
    return merge(
      {
        babelOptions: {filename: getPath(filename, fixture)},
      },
      testConfig,
      {
        babelOptions: {plugins: [[plugin, testOptions]]},
        title: fullTitle,
        code: stripIndent(code).trim(),
        output: stripIndent(output).trim(),
      },
    )
  }
}

const createFixtureTests = (fixturesDir, options) => {
  fs.readdirSync(fixturesDir).forEach(caseName => {
    const fixtureDir = path.join(fixturesDir, caseName)
    const jsCodePath = path.join(fixtureDir, 'code.js')
    const tsCodePath = path.join(fixtureDir, 'code.ts')
    const blockTitle = caseName.split('-').join(' ')
    const codePath =
      (pathExists.sync(jsCodePath) && jsCodePath) ||
      (pathExists.sync(tsCodePath) && tsCodePath)

    if (!codePath) {
      describe(blockTitle, () => {
        createFixtureTests(fixtureDir, options)
      })
      return
    }

    const ext = /\.ts$/.test(codePath) ? '.ts' : '.js'
    it(blockTitle, () => {
      const {plugin, pluginOptions, fixtureOutputName, babel, ...rest} = options

      const babelRcPath = path.join(fixtureDir, '.babelrc')

      const {babelOptions} = merge(
        {},
        fullDefaultConfig,
        {
          babelOptions: {
            plugins: [[plugin, pluginOptions]],
            // if they have a babelrc, then we'll let them use that
            // otherwise, we'll just use our simple config
            babelrc: pathExists.sync(babelRcPath),
          },
        },
        rest,
      )
      const actual = babel.transformFileSync(codePath, babelOptions).code.trim()

      const outputPath = path.join(fixtureDir, `${fixtureOutputName}${ext}`)

      if (!fs.existsSync(outputPath)) {
        fs.writeFileSync(outputPath, actual)
        return
      }

      const output = fs.readFileSync(outputPath, 'utf8').trim()

      assert.equal(
        actual,
        output,
        `actual output does not match ${fixtureOutputName}${ext}`,
      )
    })
  })
}

function testFixtures({
  title: describeBlockTitle,
  fixtures,
  filename,
  ...rest
}) {
  describe(`${describeBlockTitle} fixtures`, () => {
    const fixturesDir = getPath(filename, fixtures)
    createFixtureTests(fixturesDir, rest)
  })
}

function toTestArray(tests) {
  tests = tests || [] // null/0/false are ok, so no default param
  if (Array.isArray(tests)) {
    return tests
  }
  return Object.keys(tests).reduce((testsArray, key) => {
    let value = tests[key]
    if (typeof value === 'string') {
      value = {code: value}
    }
    testsArray.push({
      title: key,
      ...value,
    })
    return testsArray
  }, [])
}

function getCode(filename, fixture) {
  if (!fixture) {
    return ''
  }
  return fs.readFileSync(getPath(filename, fixture), 'utf8')
}

function getPath(filename, basename) {
  if (!basename) {
    return undefined
  }
  if (path.isAbsolute(basename)) {
    return basename
  }
  return path.join(path.dirname(filename), basename)
}

// eslint-disable-next-line complexity
function assertError(result, error) {
  if (typeof error === 'function') {
    if (!(result instanceof error || error(result) === true)) {
      throw result
    }
  } else if (typeof error === 'string') {
    assert.equal(result.message, error, 'Error message is incorrect')
  } else if (error instanceof RegExp) {
    assert(
      error.test(result.message),
      `Expected ${result.message} to match ${error}`,
    )
  } else {
    invariant(
      typeof error === 'boolean',
      'The given `error` must be a function, string, boolean, or RegExp',
    )
  }
}

function requiredParam(name) {
  throw new Error(`${name} is a required parameter.`)
}

function getPluginName(plugin, babel) {
  let name
  try {
    name = plugin(babel).name
  } catch (error) {
    // eslint-disable-next-line no-console
    console.error(
      oneLine`
        Attempting to infer the name of your plugin failed.
        Tried to invoke the plugin which threw the error.
      `,
    )
    throw error
  }
  invariant(name, 'The `pluginName` must be inferable or provided.')
  return name
}

/*
eslint
  complexity: "off"
*/
<commit_msg>fix: change babel-core import to optional require in default param (#33)<commit_after>import assert from 'assert'
import path from 'path'
import fs from 'fs'
import pathExists from 'path-exists'
import merge from 'lodash.merge'
import invariant from 'invariant'
import stripIndent from 'strip-indent'
import {oneLine} from 'common-tags'

const noop = () => {}

module.exports = pluginTester

const fullDefaultConfig = {
  babelOptions: {
    parserOpts: {},
    generatorOpts: {},
    babelrc: false,
  },
}

function pluginTester({
  /* istanbul ignore next (TODO: write a test for this) */
  babel = require('babel-core'),
  plugin = requiredParam('plugin'),
  pluginName = getPluginName(plugin, babel),
  title: describeBlockTitle = pluginName,
  pluginOptions,
  tests,
  fixtures,
  fixtureOutputName = 'output',
  filename,
  ...rest
} = {}) {
  let testNumber = 1
  if (fixtures) {
    testFixtures({
      plugin,
      pluginName,
      pluginOptions,
      title: describeBlockTitle,
      fixtures,
      fixtureOutputName,
      filename,
      babel,
      ...rest,
    })
  }
  const testAsArray = toTestArray(tests)
  if (!testAsArray.length) {
    return Promise.resolve()
  }
  const testerConfig = merge({}, fullDefaultConfig, rest)

  return describe(describeBlockTitle, () => {
    const promises = testAsArray.map(testConfig => {
      if (!testConfig) {
        return Promise.resolve()
      }
      const {
        skip,
        only,
        title,
        code,
        babelOptions,
        output,
        snapshot,
        error,
        setup = noop,
        teardown,
        formatResult = r => r,
      } = merge({}, testerConfig, toTestConfig(testConfig))
      assert(
        (!skip && !only) || skip !== only,
        'Cannot enable both skip and only on a test',
      )

      if (skip) {
        // eslint-disable-next-line jest/no-disabled-tests
        return it.skip(title, testerWrapper)
      } else if (only) {
        // eslint-disable-next-line jest/no-focused-tests
        return it.only(title, testerWrapper)
      } else {
        return it(title, testerWrapper)
      }

      async function testerWrapper() {
        const teardowns = teardown ? [teardown] : []
        let returnedTeardown
        try {
          returnedTeardown = await setup()
        } catch (e) {
          // eslint-disable-next-line no-console
          console.error('There was a problem during setup')
          throw e
        }
        if (typeof returnedTeardown === 'function') {
          teardowns.push(returnedTeardown)
        }
        try {
          tester()
        } finally {
          try {
            await Promise.all(teardowns.map(t => t()))
          } catch (e) {
            // eslint-disable-next-line no-console
            console.error('There was a problem during teardown')
            // eslint-disable-next-line no-unsafe-finally
            throw e
          }
        }
      }

      // eslint-disable-next-line complexity
      function tester() {
        invariant(
          code,
          oneLine`
            A string or object with a \`code\` or
            \`fixture\` property must be provided
          `,
        )
        invariant(
          !babelOptions.babelrc || babelOptions.filename,
          'babelrc set to true, but no filename specified in babelOptions',
        )
        invariant(
          !snapshot || !output,
          '`output` cannot be provided with `snapshot: true`',
        )

        let result
        let errored = false

        try {
          result = formatResult(babel.transform(code, babelOptions).code.trim())
        } catch (err) {
          if (error) {
            errored = true
            result = err
          } else {
            throw err
          }
        }

        const expectedToThrowButDidNot = error && !errored
        assert(
          !expectedToThrowButDidNot,
          'Expected to throw error, but it did not.',
        )

        if (snapshot) {
          invariant(
            result !== code,
            oneLine`
              Code was unmodified but attempted to take a snapshot.
              If the code should not be modified, set \`snapshot: false\`
            `,
          )
          const separator = '\n\n      ↓ ↓ ↓ ↓ ↓ ↓\n\n'
          const formattedOutput = [code, separator, result].join('')
          expect(`\n${formattedOutput}\n`).toMatchSnapshot(title)
        } else if (error) {
          assertError(result, error)
        } else if (output) {
          assert.equal(result, output, 'Output is incorrect.')
        } else {
          assert.equal(
            result,
            code,
            'Expected output to not change, but it did',
          )
        }
      }
    })

    return Promise.all(promises)
  })

  function toTestConfig(testConfig) {
    if (typeof testConfig === 'string') {
      testConfig = {code: testConfig}
    }
    const {
      title,
      fixture,
      code = getCode(filename, fixture),
      fullTitle = title || `${testNumber++}. ${pluginName}`,
      output = getCode(filename, testConfig.outputFixture),
      pluginOptions: testOptions = pluginOptions,
    } = testConfig
    return merge(
      {
        babelOptions: {filename: getPath(filename, fixture)},
      },
      testConfig,
      {
        babelOptions: {plugins: [[plugin, testOptions]]},
        title: fullTitle,
        code: stripIndent(code).trim(),
        output: stripIndent(output).trim(),
      },
    )
  }
}

const createFixtureTests = (fixturesDir, options) => {
  fs.readdirSync(fixturesDir).forEach(caseName => {
    const fixtureDir = path.join(fixturesDir, caseName)
    const jsCodePath = path.join(fixtureDir, 'code.js')
    const tsCodePath = path.join(fixtureDir, 'code.ts')
    const blockTitle = caseName.split('-').join(' ')
    const codePath =
      (pathExists.sync(jsCodePath) && jsCodePath) ||
      (pathExists.sync(tsCodePath) && tsCodePath)

    if (!codePath) {
      describe(blockTitle, () => {
        createFixtureTests(fixtureDir, options)
      })
      return
    }

    const ext = /\.ts$/.test(codePath) ? '.ts' : '.js'
    it(blockTitle, () => {
      const {plugin, pluginOptions, fixtureOutputName, babel, ...rest} = options

      const babelRcPath = path.join(fixtureDir, '.babelrc')

      const {babelOptions} = merge(
        {},
        fullDefaultConfig,
        {
          babelOptions: {
            plugins: [[plugin, pluginOptions]],
            // if they have a babelrc, then we'll let them use that
            // otherwise, we'll just use our simple config
            babelrc: pathExists.sync(babelRcPath),
          },
        },
        rest,
      )
      const actual = babel.transformFileSync(codePath, babelOptions).code.trim()

      const outputPath = path.join(fixtureDir, `${fixtureOutputName}${ext}`)

      if (!fs.existsSync(outputPath)) {
        fs.writeFileSync(outputPath, actual)
        return
      }

      const output = fs.readFileSync(outputPath, 'utf8').trim()

      assert.equal(
        actual,
        output,
        `actual output does not match ${fixtureOutputName}${ext}`,
      )
    })
  })
}

function testFixtures({
  title: describeBlockTitle,
  fixtures,
  filename,
  ...rest
}) {
  describe(`${describeBlockTitle} fixtures`, () => {
    const fixturesDir = getPath(filename, fixtures)
    createFixtureTests(fixturesDir, rest)
  })
}

function toTestArray(tests) {
  tests = tests || [] // null/0/false are ok, so no default param
  if (Array.isArray(tests)) {
    return tests
  }
  return Object.keys(tests).reduce((testsArray, key) => {
    let value = tests[key]
    if (typeof value === 'string') {
      value = {code: value}
    }
    testsArray.push({
      title: key,
      ...value,
    })
    return testsArray
  }, [])
}

function getCode(filename, fixture) {
  if (!fixture) {
    return ''
  }
  return fs.readFileSync(getPath(filename, fixture), 'utf8')
}

function getPath(filename, basename) {
  if (!basename) {
    return undefined
  }
  if (path.isAbsolute(basename)) {
    return basename
  }
  return path.join(path.dirname(filename), basename)
}

// eslint-disable-next-line complexity
function assertError(result, error) {
  if (typeof error === 'function') {
    if (!(result instanceof error || error(result) === true)) {
      throw result
    }
  } else if (typeof error === 'string') {
    assert.equal(result.message, error, 'Error message is incorrect')
  } else if (error instanceof RegExp) {
    assert(
      error.test(result.message),
      `Expected ${result.message} to match ${error}`,
    )
  } else {
    invariant(
      typeof error === 'boolean',
      'The given `error` must be a function, string, boolean, or RegExp',
    )
  }
}

function requiredParam(name) {
  throw new Error(`${name} is a required parameter.`)
}

function getPluginName(plugin, babel) {
  let name
  try {
    name = plugin(babel).name
  } catch (error) {
    // eslint-disable-next-line no-console
    console.error(
      oneLine`
        Attempting to infer the name of your plugin failed.
        Tried to invoke the plugin which threw the error.
      `,
    )
    throw error
  }
  invariant(name, 'The `pluginName` must be inferable or provided.')
  return name
}

/*
eslint
  complexity: "off"
*/
<|endoftext|>