css-syntax-error.js 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. 'use strict'
  2. let pico = require('picocolors')
  3. let terminalHighlight = require('./terminal-highlight')
  4. class CssSyntaxError extends Error {
  5. constructor(message, line, column, source, file, plugin) {
  6. super(message)
  7. this.name = 'CssSyntaxError'
  8. this.reason = message
  9. if (file) {
  10. this.file = file
  11. }
  12. if (source) {
  13. this.source = source
  14. }
  15. if (plugin) {
  16. this.plugin = plugin
  17. }
  18. if (typeof line !== 'undefined' && typeof column !== 'undefined') {
  19. if (typeof line === 'number') {
  20. this.line = line
  21. this.column = column
  22. } else {
  23. this.line = line.line
  24. this.column = line.column
  25. this.endLine = column.line
  26. this.endColumn = column.column
  27. }
  28. }
  29. this.setMessage()
  30. if (Error.captureStackTrace) {
  31. Error.captureStackTrace(this, CssSyntaxError)
  32. }
  33. }
  34. setMessage() {
  35. this.message = this.plugin ? this.plugin + ': ' : ''
  36. this.message += this.file ? this.file : '<css input>'
  37. if (typeof this.line !== 'undefined') {
  38. this.message += ':' + this.line + ':' + this.column
  39. }
  40. this.message += ': ' + this.reason
  41. }
  42. showSourceCode(color) {
  43. if (!this.source) return ''
  44. let css = this.source
  45. if (color == null) color = pico.isColorSupported
  46. if (terminalHighlight) {
  47. if (color) css = terminalHighlight(css)
  48. }
  49. let lines = css.split(/\r?\n/)
  50. let start = Math.max(this.line - 3, 0)
  51. let end = Math.min(this.line + 2, lines.length)
  52. let maxWidth = String(end).length
  53. let mark, aside
  54. if (color) {
  55. let { bold, gray, red } = pico.createColors(true)
  56. mark = text => bold(red(text))
  57. aside = text => gray(text)
  58. } else {
  59. mark = aside = str => str
  60. }
  61. return lines
  62. .slice(start, end)
  63. .map((line, index) => {
  64. let number = start + 1 + index
  65. let gutter = ' ' + (' ' + number).slice(-maxWidth) + ' | '
  66. if (number === this.line) {
  67. let spacing =
  68. aside(gutter.replace(/\d/g, ' ')) +
  69. line.slice(0, this.column - 1).replace(/[^\t]/g, ' ')
  70. return mark('>') + aside(gutter) + line + '\n ' + spacing + mark('^')
  71. }
  72. return ' ' + aside(gutter) + line
  73. })
  74. .join('\n')
  75. }
  76. toString() {
  77. let code = this.showSourceCode()
  78. if (code) {
  79. code = '\n\n' + code + '\n'
  80. }
  81. return this.name + ': ' + this.message + code
  82. }
  83. }
  84. module.exports = CssSyntaxError
  85. CssSyntaxError.default = CssSyntaxError