EspreeFacade.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. import * as espree from 'espree';
  2. import * as ESTree from 'estree';
  3. import chalk, { Chalk } from 'chalk';
  4. /**
  5. * Facade over `espree`
  6. */
  7. export class EspreeFacade {
  8. /**
  9. * @type {Chalk}
  10. */
  11. private static readonly colorError: Chalk = chalk.red;
  12. /**
  13. * @type {number}
  14. */
  15. private static readonly nearestSymbolsCount: number = 15;
  16. /**
  17. * @type {SourceType[]}
  18. */
  19. private static readonly sourceTypes: espree.SourceType[] = [
  20. 'script',
  21. 'module'
  22. ];
  23. /**
  24. * @param {string} input
  25. * @param {Options} config
  26. * @returns {Program}
  27. */
  28. public static parse (input: string, config: espree.ParseOptions): ESTree.Program | never {
  29. const sourceTypeLength: number = EspreeFacade.sourceTypes.length;
  30. for (let i: number = 0; i < sourceTypeLength; i++) {
  31. try {
  32. return EspreeFacade.parseType(input, config, EspreeFacade.sourceTypes[i]);
  33. } catch (error) {
  34. if (i < sourceTypeLength - 1) {
  35. continue;
  36. }
  37. throw new Error(EspreeFacade.processParsingError(
  38. input,
  39. error.message,
  40. {
  41. line: error.lineNumber,
  42. column: error.column,
  43. }
  44. ));
  45. }
  46. }
  47. throw new Error(`Espree parsing error`);
  48. }
  49. /**
  50. * @param {string} input
  51. * @param {ParseOptions} inputConfig
  52. * @param {SourceType} sourceType
  53. * @returns {Program}
  54. */
  55. private static parseType (
  56. input: string,
  57. inputConfig: espree.ParseOptions,
  58. sourceType: espree.SourceType
  59. ): ESTree.Program {
  60. const config: espree.ParseOptions = { ...inputConfig, sourceType };
  61. return espree.parse(input, config);
  62. }
  63. /**
  64. * @param {string} sourceCode
  65. * @param {string} errorMessage
  66. * @param {Position} position
  67. * @returns {never}
  68. */
  69. private static processParsingError (sourceCode: string, errorMessage: string, position: ESTree.Position | null): never {
  70. if (!position || !position.line || !position.column) {
  71. throw new Error(errorMessage);
  72. }
  73. const sourceCodeLines: string[] = sourceCode.split(/\r?\n/);
  74. const errorLine: string | undefined = sourceCodeLines[position.line - 1];
  75. if (!errorLine) {
  76. throw new Error(errorMessage);
  77. }
  78. const startErrorIndex: number = Math.max(0, position.column - EspreeFacade.nearestSymbolsCount);
  79. const endErrorIndex: number = Math.min(errorLine.length, position.column + EspreeFacade.nearestSymbolsCount);
  80. const formattedPointer: string = EspreeFacade.colorError('>');
  81. const formattedCodeSlice: string = `...${
  82. errorLine.substring(startErrorIndex, endErrorIndex).replace(/^\s+/, '')
  83. }...`;
  84. throw new Error(`Line ${position.line}: ${errorMessage}\n${formattedPointer} ${formattedCodeSlice}`);
  85. }
  86. }