Skip to content

Commit

Permalink
feat: include path for duplicate key exception (#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
P0lip authored Jul 15, 2019
1 parent badb1ef commit a10d450
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 47 deletions.
3 changes: 3 additions & 0 deletions src/__tests__/parseWithPointers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ european-cities: &cities
{
code: 'YAMLException',
message: 'duplicate key',
path: ['foo'],
range: {
start: {
line: 1,
Expand Down Expand Up @@ -271,6 +272,7 @@ european-cities: &cities
{
code: 'YAMLException',
message: 'duplicate key',
path: ['baz', 'duplicated'],
range: {
start: {
line: 3,
Expand All @@ -286,6 +288,7 @@ european-cities: &cities
{
code: 'YAMLException',
message: 'duplicate key',
path: ['baz', 'duplicated'],
range: {
start: {
line: 4,
Expand Down
46 changes: 46 additions & 0 deletions src/buildJsonPath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { JsonPath } from '@stoplight/types';
import { Kind, YAMLMapping, YAMLNode, YAMLScalar, YAMLSequence } from 'yaml-ast-parser';
import { isValidNode } from './utils';

export function buildJsonPath(node: YAMLNode) {
const path: JsonPath = [];

let prevNode: YAMLNode = node;

while (node) {
switch (node.kind) {
case Kind.SCALAR:
path.unshift((node as YAMLScalar).value);
break;
case Kind.MAPPING:
if (prevNode !== (node as YAMLMapping).key) {
if (
path.length > 0 &&
isValidNode((node as YAMLMapping).value) &&
(node as YAMLMapping).value.value === path[0]
) {
path[0] = (node as YAMLMapping).key.value;
} else {
path.unshift((node as YAMLMapping).key.value);
}
}
break;
case Kind.SEQ:
if (prevNode) {
const index = (node as YAMLSequence).items.indexOf(prevNode);
if (prevNode.kind === Kind.SCALAR) {
path[0] = index;
// always better to point to parent node rather than nothing
} else if (index !== -1) {
path.unshift(index);
}
}
break;
}

prevNode = node;
node = node.parent;
}

return path;
}
51 changes: 4 additions & 47 deletions src/getJsonPathForPosition.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { GetJsonPathForPosition, JsonPath } from '@stoplight/types';
import { Kind, YamlMap, YAMLMapping, YAMLNode, YAMLScalar, YAMLSequence } from 'yaml-ast-parser';
import { GetJsonPathForPosition } from '@stoplight/types';
import { Kind, YamlMap, YAMLMapping, YAMLNode, YAMLSequence } from 'yaml-ast-parser';
import { buildJsonPath } from './buildJsonPath';
import { isValidNode } from './utils';

export const getJsonPathForPosition: GetJsonPathForPosition<YAMLNode, number[]> = (
{ ast, lineMap },
Expand All @@ -19,8 +21,6 @@ export const getJsonPathForPosition: GetJsonPathForPosition<YAMLNode, number[]>
return path;
};

const isValidNode = (node: YAMLNode) => node !== null && node !== undefined;

function* walk(node: YAMLNode): IterableIterator<YAMLNode> {
switch (node.kind) {
case Kind.MAP:
Expand Down Expand Up @@ -115,46 +115,3 @@ function findClosestScalar(container: YAMLNode, offset: number, line: number, li

return container;
}

function buildJsonPath(node: YAMLNode) {
const path: JsonPath = [];

let prevNode: YAMLNode = node;

while (node) {
switch (node.kind) {
case Kind.SCALAR:
path.unshift((node as YAMLScalar).value);
break;
case Kind.MAPPING:
if (prevNode !== (node as YAMLMapping).key) {
if (
path.length > 0 &&
isValidNode((node as YAMLMapping).value) &&
(node as YAMLMapping).value.value === path[0]
) {
path[0] = (node as YAMLMapping).key.value;
} else {
path.unshift((node as YAMLMapping).key.value);
}
}
break;
case Kind.SEQ:
if (prevNode) {
const index = (node as YAMLSequence).items.indexOf(prevNode);
if (prevNode.kind === Kind.SCALAR) {
path[0] = index;
// always better to point to parent node rather than nothing
} else if (index !== -1) {
path.unshift(index);
}
}
break;
}

prevNode = node;
node = node.parent;
}

return path;
}
2 changes: 2 additions & 0 deletions src/parseWithPointers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
YAMLNode,
YAMLSequence,
} from 'yaml-ast-parser';
import { buildJsonPath } from './buildJsonPath';
import { lineForPosition } from './lineForPosition';
import { YamlParserResult } from './types';

Expand Down Expand Up @@ -184,6 +185,7 @@ const transformDuplicatedMappingKeys = (nodes: YAMLNode[], lineMap: number[]): I
validations.push({
code: 'YAMLException',
message: 'duplicate key',
path: buildJsonPath(node),
range: {
start: {
line: startLine,
Expand Down
3 changes: 3 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { YAMLNode } from 'yaml-ast-parser';

export const isValidNode = (node: YAMLNode) => node !== null && node !== undefined;

0 comments on commit a10d450

Please sign in to comment.