Repository: parcel-bundler/parcel
File: packages/core/diagnostic/src/diagnostic.js (JavaScript)

Code before:

    // Construct error from diagnostics
    super(diagnostics[0].message);
    this.stack = diagnostics[0].stack ?? super.stack;
    this.name = diagnostics[0].name ?? super.name;

    this.diagnostics = diagnostics;
  }
}

/**
 * Turns a list of positions in a JSON file with messages into a list of diagnostics.
 * Uses <a href="https://github.com/epoberezkin/json-source-map">epoberezkin/json-source-map</a>.
 *
 * @param code the JSON code
 * @param ids A list of JSON keypaths (<code>key: "/some/parent/child"</code>) with corresponding messages, \
 * <code>type</code> signifies whether the key of the value in a JSON object should be highlighted.
 */
export function generateJSONCodeHighlights(
  code: string,
  ids: Array<{|key: string, type?: ?'key' | 'value', message?: string|}>,
): Array<DiagnosticCodeHighlight> {
  // json-source-map doesn't support a tabWidth option (yet)
  let map = jsonMap.parse(code.replace(/\t/g, ' '));
  return ids.map(({key, type, message}) => {
    let pos = map.pointers[key];
    return {
      ...getJSONSourceLocation(pos, type),
      message,
    };
  });
}

/**
 * Converts entries in <a href="https://github.com/epoberezkin/json-source-map">epoberezkin/json-source-map</a>'s
 * <code>result.pointers</code> array.
 */
export function getJSONSourceLocation(
  pos: Mapping,
  type?: ?'key' | 'value',
): {|
  start: DiagnosticHighlightLocation,
  end: DiagnosticHighlightLocation,
|} {
  if (!type && pos.key && pos.value) {
    // key and value
    return {
      start: {line: pos.key.line + 1, column: pos.key.column + 1},
      end: {line: pos.valueEnd.line + 1, column: pos.valueEnd.column},
    };
  } else if (type == 'key' || !pos.value) {

Review comment (suggestion):
I'm afraid that Flow cannot always be trusted 😬 

`item` isn't a number in this case, but Flow doesn't complain.
```
type Pointers =  {|[key: string]: number|};

let x: Pointers = {b: 1};

let item = x['a'];
(item: number);
```

https://flow.org/try/#0C4TwDgpgBACg9gSwHbAgJwM5QLxSgbwB8BtAawhAC4oNg1kBzAXWqQFcBbAI3UIF8A3ACghAGwjAoAD2rxkqTDgKCR4yQlQclU4gHIAhrqbCAFBogdWnHmgCUAoA

In our situation here, the `pos` parameter of `getJSONSourceLocation` could suddenly become `undefined`, although Flow says it's an object.

Code after:
      : [opts.diagnostic];

    // Construct error from diagnostics
    super(diagnostics[0].message);
    this.stack = diagnostics[0].stack ?? super.stack;
    this.name = diagnostics[0].name ?? super.name;

    this.diagnostics = diagnostics;
  }
}

/**
 * Turns a list of positions in a JSON file with messages into a list of diagnostics.
 * Uses <a href="https://github.com/epoberezkin/json-source-map">epoberezkin/json-source-map</a>.
 *
 * @param code the JSON code
 * @param ids A list of JSON keypaths (<code>key: "/some/parent/child"</code>) with corresponding messages, \
 * <code>type</code> signifies whether the key of the value in a JSON object should be highlighted.
 */
export function generateJSONCodeHighlights(
  code: string,
  ids: Array<{|key: string, type?: ?'key' | 'value', message?: string|}>,
): Array<DiagnosticCodeHighlight> {
  // json-source-map doesn't support a tabWidth option (yet)
  let map = jsonMap.parse(code.replace(/\t/g, ' '));
  return ids.map(({key, type, message}) => {
    let pos = nullthrows(map.pointers[key]);
    return {
      ...getJSONSourceLocation(pos, type),
      message,
    };
  });
}

/**
 * Converts entries in <a href="https://github.com/epoberezkin/json-source-map">epoberezkin/json-source-map</a>'s
 * <code>result.pointers</code> array.
 */
export function getJSONSourceLocation(
  pos: Mapping,
  type?: ?'key' | 'value',
): {|
  start: DiagnosticHighlightLocation,
  end: DiagnosticHighlightLocation,
|} {
  if (!type && pos.key && pos.value) {
    // key and value
    return {
      start: {line: pos.key.line + 1, column: pos.key.column + 1},
      end: {line: pos.valueEnd.line + 1, column: pos.valueEnd.column},
    };