从敖天羽老师那儿看到的题目,链接在 这儿

这个题目要求拿到错误信息和错误堆栈。错误信息比较简单,首先对错误信息字符串做一下分割,这样第一个元素就是错误信息,题目要求不要 TypeError 这种错误类型,直接一个正则替换解决。

接下来是错误堆栈,堆栈有6种形式,FireFox 和 Chrome各三种:

// Chrome
at bar http://192.168.31.8:8000/c.js:2:9
at <anonymous>:1:11
at http://192.168.31.8:8000/a.js:22:3

// Firefox
bar@http://192.168.31.8:8000/c.js:2:9
<anonymous>:1:11
http://192.168.31.8:8000/a.js:22:3

题目需要文件名、行、列,一个很简单的正则表达式 /(https?\:.*\.js):(\d+):(\d+)/ 就能完成题目要求了,匹配到就通过 match 提取信息,没匹配到就说明是 anonymous 行,直接过滤掉。

这样做其实很简单,但是错误信息是不完整的,所以我自己加了点难度,需要同时提取函数名。问题复杂化之后我们需要观察函数名的几种情况:

  • at bar http://192.168.31.8:8000/c.js:2:9
  • at http://192.168.31.8:8000/a.js:22:3
  • bar@http://192.168.31.8:8000/c.js:2:9
  • http://192.168.31.8:8000/c.js:2:9

匹配需要将Firefox和Chrome下的情况分开。Chrome下的情况比较复杂,需要将 at 排除掉,Firefox下直接匹配 @ 之前的字符就可以了。最终匹配函数名的正则是这样:

/((\w[\w\d\_\.]*)@|at ((\w[\w\d\_\.]*) )*)?/

最后放到一起就是:

const reg = /((\w[\w\d\_\.]*)@|at ((\w[\w\d\_\.]*) )*)?(https?:\/\/.*\.js):(\d+):(\d+)/

最终通过捕获组统一拿到函数名信息。

下面的完整的代码实现:

const errRegexp = /((\w[\w\d\_\.]*)@|at ((\w[\w\d\_\.]*) )*)?(https?:\/\/.*\.js):(\d+):(\d+)/

function parseError (err) {
  const lines = err.split('\n')
  const message = lines[0].replace(/^\w+\: /, '')
  const stack = lines.slice(1).map(info => {
    const match = info.match(errRegexp)
    if (!match) return null
    let [filename, line, column] = match.slice(5)
    let functionname = match[2] || match[4]
    return {
      line: parseInt(line),
      column: parseInt(column),
      filename,
      functionname
    }
  }).filter(l => !!l)
  return {
    message,
    stack
  }
}

const expect = (casename, err, trace) => {
  const parsed = parseError(err)
  // 为了快速验证,直接使用序列化函数比较
  const isMatch = JSON.stringify(parsed) === JSON.stringify(trace)
  if (!isMatch) {
    console.log(casename, '测试未通过')
  } else {
    console.log(casename, '测试通过')
  }
}

expect(
  '测试',
  `TypeError: Error raised
    foo@http://192.168.31.8:8000/c.js:2:9
    foo.bar@http://192.168.31.8:8000/c.js:2:9
    http://192.168.31.8:8000/c.js:2:9
    at bar http://192.168.31.8:8000/c.js:2:9
    at bar.foo http://192.168.31.8:8000/c.js:2:9
    at <anonymous>:1:11
    at http://192.168.31.8:8000/a.js:22:3`,
  {
    message: 'Error raised',
    stack: [
      {
        line: 2,
        column: 9,
        filename: 'http://192.168.31.8:8000/c.js',
        functionname: 'foo'
      },
      {
        line: 2,
        column: 9,
        filename: 'http://192.168.31.8:8000/c.js',
        functionname: 'foo.bar'
      },
      {
        line: 2,
        column: 9,
        filename: 'http://192.168.31.8:8000/c.js'
      },
      {
        line: 2,
        column: 9,
        filename: 'http://192.168.31.8:8000/c.js',
        functionname: 'bar'
      },
      {
        line: 2,
        column: 9,
        filename: 'http://192.168.31.8:8000/c.js',
        functionname: 'bar.foo'
      },
      {
        line: 22,
        column: 3,
        filename: 'http://192.168.31.8:8000/a.js'
      }
    ]
  }
)

至于题目其它要求的 lintformatCI 部分就不在这儿写了,有兴趣的同学可以去找资料看。

参考