text: const fs = require('fs')
  const { SourceMapConsumer } = require('source-map')
  const path = require('path')
  const pify = require('pify')
  
  const fsAsync = pify(fs)
  
  //
  // Utility to help check if sourcemaps are working
  //
  // searches `dist/chrome/inpage.js` for "new Error" statements
  // and prints their source lines using the sourcemaps.
  // if not working it may error or print minified garbage
  //
  
  start().catch((error) => {
    console.error(error)
    process.exit(1)
  })
  
  
  async function start () {
    const targetFiles = [
      `background.js`,
      // `bg-libs`, skipped because source maps are invalid due to browserify bug: https://github.com/browserify/browserify/issues/1971
      // `contentscript.js`, skipped because the validator is erroneously sampling the inlined `inpage.js` script
      `inpage.js`,
      'phishing-detect.js',
      `ui.js`,
      // `ui-libs.js`, skipped because source maps are invalid due to browserify bug: https://github.com/browserify/browserify/issues/1971
    ]
    let valid = true
  
    for (const buildName of targetFiles) {
      const fileIsValid = await validateSourcemapForFile({ buildName })
      valid = valid && fileIsValid
    }
  
    if (!valid) {
      process.exit(1)
    }
  }
  
  async function validateSourcemapForFile ({ buildName }) {
    console.log(`build "${buildName}"`)
    const platform = `chrome`
    // load build and sourcemaps
    let rawBuild
    try {
      const filePath = path.join(__dirname, `/../dist/${platform}/`, `${buildName}`)
      rawBuild = await fsAsync.readFile(filePath, 'utf8')
    } catch (err) {}
    if (!rawBuild) {
      throw new Error(`SourcemapValidator - failed to load source file for "${buildName}"`)
    }
    // attempt to load in dist mode
    let rawSourceMap
    try {
      const filePath = path.join(__dirname, `/../dist/sourcemaps/`, `${buildName}.map`)
      rawSourceMap = await fsAsync.readFile(filePath, 'utf8')
    } catch (err) {}
    // attempt to load in dev mode
    try {
      const filePath = path.join(__dirname, `/../dist/${platform}/`, `${buildName}.map`)
      rawSourceMap = await fsAsync.readFile(filePath, 'utf8')
    } catch (err) {}
    if (!rawSourceMap) {
      throw new Error(`SourcemapValidator - failed to load sourcemaps for "${buildName}"`)
    }
  
    const consumer = await new SourceMapConsumer(rawSourceMap)
  
    const hasContentsOfAllSources = consumer.hasContentsOfAllSources()
    if (!hasContentsOfAllSources) {
      console.warn('SourcemapValidator - missing content of some sources...')
    }
  
    console.log(`  sampling from ${consumer.sources.length} files`)
    let sampleCount = 0
    let valid = true
  
    const buildLines = rawBuild.split('\n')
    const targetString = 'new Error'
    // const targetString = 'null'
    const matchesPerLine = buildLines.map((line) => indicesOf(targetString, line))
    matchesPerLine.forEach((matchIndices, lineIndex) => {
      matchIndices.forEach((matchColumn) => {
        sampleCount++
        const position = { line: lineIndex + 1, column: matchColumn }
        const result = consumer.originalPositionFor(position)
        // warn if source content is missing
        if (!result.source) {
          valid = false
          console.warn(`!! missing source for position: ${JSON.stringify(position)}`)
          // const buildLine = buildLines[position.line - 1]
          console.warn(`   origin in build:`)
          console.warn(`   ${buildLines[position.line - 2]}`)
          console.warn(`-> ${buildLines[position.line - 1]}`)
          console.warn(`   ${buildLines[position.line - 0]}`)
          return
        }
        const sourceContent = consumer.sourceContentFor(result.source)
        const sourceLines = sourceContent.split('\n')
        const line = sourceLines[result.line - 1]
        // this sometimes includes the whole line though we tried to match somewhere in the middle
        const portion = line.slice(result.column)
        const isMaybeValid = portion.includes(targetString)
        if (!isMaybeValid) {
          valid = false
          console.error(`Sourcemap seems invalid:\n${getFencedCode(result.source, line)}`)
        }
      })
    })
    console.log(`  checked ${sampleCount} samples`)
    return valid
  }
  
  const CODE_FENCE_LENGTH = 80
  const TITLE_PADDING_LENGTH = 1
  
  function getFencedCode (filename, code) {
    const title = `${' '.repeat(TITLE_PADDING_LENGTH)}${filename}${' '.repeat(TITLE_PADDING_LENGTH)}`
    const openingFenceLength = Math.max(CODE_FENCE_LENGTH - (filename.length + (TITLE_PADDING_LENGTH * 2)), 0)
    const startOpeningFenceLength = Math.floor(openingFenceLength / 2)
    const endOpeningFenceLength = Math.ceil(openingFenceLength / 2)
    const openingFence = `${'='.repeat(startOpeningFenceLength)}${title}${'='.repeat(endOpeningFenceLength)}`
    const closingFence = '='.repeat(CODE_FENCE_LENGTH)
  
    return `${openingFence}\n${code}\n${closingFence}\n`
  }
  
  function indicesOf (substring, string) {
    const a = []
    let i = -1
    while ((i = string.indexOf(substring, i + 1)) >= 0) {
      a.push(i)
    }
    return a
  }
  