/**
 * If the given exception is an error with code location information, extract
 * its start and end position and return a PatchError to use in its place.
 * Otherwise, return null.
 */
// tslint:disable-next-line no-any
export default function resolveToPatchError(err: any, content: string, stageName: string): PatchError | null {
  let makePatchError = (start: number, end: number, source: string) =>
    new PatchError(`${stageName} failed to parse: ${err.message}`, source, start, end);

  if (err.pos) {
    // Handle JavaScript parse errors.
    let { pos } = err;
    if (pos === content.length) {
      pos--;
    }
    // In most cases, we can use the source code we already have, but for
    // esnext, the code might be an intermediate code state, so use that from
    // the exception if possible.
    let source = err.source || content;
    return makePatchError(pos, pos + 1, source);
  } else if (err.syntaxError) {
    // Handle CoffeeScript parse errors.
    let { first_line, first_column, last_line, last_column } = err.syntaxError.location;
    let lineMap = new LinesAndColumns(content);
    let firstIndex = lineMap.indexForLocation({ line: first_line, column: first_column });
    let lastIndex = lineMap.indexForLocation({ line: last_line, column: last_column });
    if (firstIndex !== null) {
      if (lastIndex === null) {
        lastIndex = firstIndex + 1;
      } else {
        lastIndex++;
      }
      return makePatchError(firstIndex, lastIndex, content);
    }
  }
  return null;
}
Beispiel #2
0
  static prettyPrint(error: PatchError): string {
    let { source, start, end, message } = error;
    start = Math.min(Math.max(start, 0), source.length);
    end = Math.min(Math.max(end, start), source.length);
    let lineMap = new LinesAndColumns(source);
    let startLoc = lineMap.locationForIndex(start);
    let endLoc = lineMap.locationForIndex(end);

    if (!startLoc || !endLoc) {
      throw new Error(`unable to find locations for range: [${start}, ${end})`);
    }

    let displayStartLine = Math.max(0, startLoc.line - 2);
    let displayEndLine = endLoc.line + 2;

    let rows: Array<Array<string>> = [];

    for (let line = displayStartLine; line <= displayEndLine; line++) {
      let startOfLine = lineMap.indexForLocation({ line, column: 0 });
      let endOfLine = lineMap.indexForLocation({ line: line + 1, column: 0 });
      if (startOfLine === null) {
        break;
      }
      if (endOfLine === null) {
        endOfLine = source.length;
      }
      let lineSource = trimRight(source.slice(startOfLine, endOfLine));
      if (startLoc.line !== endLoc.line) {
        if (line >= startLoc.line && line <= endLoc.line) {
          rows.push([`>`, `${line + 1} |`, lineSource]);
        } else {
          rows.push([``, `${line + 1} |`, lineSource]);
        }
      } else if (line === startLoc.line) {
        let highlightLength = Math.max(endLoc.column - startLoc.column, 1);
        rows.push(
          [`>`, `${line + 1} |`, lineSource],
          [``, `|`, ' '.repeat(startLoc.column) + '^'.repeat(highlightLength)]
        );
      } else {
        rows.push([``, `${line + 1} |`, lineSource]);
      }
    }

    let columns: Array<Column> = [
      { id: 'marker', align: 'right' },
      { id: 'line', align: 'right' },
      { id: 'source', align: 'left' }
    ];

    return `${message}\n${printTable({ rows, columns })}`;
  }
  getRange(locatable: Base | LocationData): [number, number] | null {
    if (locatable instanceof Base) {
      return this.getRange(locatable.locationData);
    } else {
      let locationData = locatable as LocationData;
      let start = this.linesAndColumns.indexForLocation({ line: locationData.first_line, column: locationData.first_column });
      let end = this.linesAndColumns.indexForLocation({ line: locationData.last_line, column: locationData.last_column });

      if (start === null || end === null) {
        return null;
      }

      return [start, end + 1];
    }
  }
/**
 * Assumes first_line/first_column are correct.
 */
export default function fixInvalidLocationData(locationData: LocationData, linesAndColumns: LinesAndColumns): LocationData {
  let { last_line, last_column } = locationData;
  let indexForLocation = linesAndColumns.indexForLocation({ line: last_line, column: last_column });

  if (indexForLocation !== null) {
    return locationData;
  } else {
    let offset = 1;

    for (;;) {
      let index = linesAndColumns.indexForLocation({ line: last_line, column: last_column - offset });

      offset++;

      if (index !== null) {
        let location = linesAndColumns.locationForIndex(index + offset);

        if (!location) {
          throw new Error(
            `Unable to determine adjustment offset for incorrect location data: ` +
            `${JSON.stringify(locationData)}. No valid location found for index: ` +
            `${index + offset}`
          );
        }

        last_line = location.line;
        last_column = location.column;
        break;
      }
    }

    return {
      ...locationData,
      last_line,
      last_column
    };
  }
}