function parseTemplate (str) {
  const fields = []
  let char = ''
  let pos = 0
  let prefix = ''
  let match = null
  let input

  while (true) { // eslint-disable-line no-constant-condition
    char = str[pos]

    if (!char) {
      fields.push({
        prefix: prefix,
        placeholder: null
      })
      break
    } else if (char === '[') {
      input = str.slice(pos)
      match = SIMPLE_PLACEHOLDER_RX.exec(input)
      if (match) {
        fields.push({
          prefix: prefix,
          placeholder: match[1].toLowerCase()
        })
        pos += match[0].length
        prefix = ''
        continue
      }

      match = HASH_PLACEHOLDER_RX.exec(input)
      if (match) {
        fields.push({
          prefix: prefix,
          placeholder: match[1].toLowerCase(),
          width: parseInt(match[2] || 0, 10)
        })
        pos += match[0].length
        prefix = ''
        continue
      }
    }
    prefix += char
    pos++
  }

  return fields
}
Loop over the template string and return an array of objects in the form:
{
    prefix: 'literal text',
    placeholder: 'replacement field name'
    [, width: maximum hash length for hash & chunkhash placeholders]
}

The values in the object conceptually represent a span of literal text followed by a single replacement field.
If there is no literal text (which can happen if two replacement fields occur consecutively),
then prefix will be an empty string.
If there is no replacement field, then the value of placeholder will be null.
If the value of placeholder is either 'hash', 'fullhash', or 'chunkhash', then width will be a positive integer.
Otherwise it will be left undefined.