Skip to content

Commit d9e6530

Browse files
authored
feat: implement getFirstPrimitiveProperty (#34)
* feat: implement getFirstPrimitiveProperty * fix: moar errors
1 parent ec76ac8 commit d9e6530

File tree

4 files changed

+77
-1
lines changed

4 files changed

+77
-1
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
"release": "sl-scripts release",
3434
"release.dryRun": "sl-scripts release --dry-run --debug",
3535
"test": "jest",
36-
"test.prod": "yarn lint && yarn test --coverage --no-cache",
36+
"test.prod": "yarn lint && yarn test --coverage --no-cache --max-workers=2",
3737
"test.update": "yarn test --updateSnapshot",
3838
"test.watch": "yarn test --watch"
3939
},
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { getFirstPrimitiveProperty } from '../getFirstPrimitiveProperty';
2+
3+
describe('getFirstPrimitiveProperty', () => {
4+
it.each(['true', 'null', '[]', '{}', '{"a":[]}', '{"a":{}}'])(
5+
'returns nothing when no valid property is found %s',
6+
text => {
7+
expect(getFirstPrimitiveProperty(text)).toBeUndefined();
8+
},
9+
);
10+
11+
it.each(['{', '{2', '{"d', '{"dd"dd', '{"dd":', '{"d": fa'])('throws SyntaxError given %s', text => {
12+
expect(getFirstPrimitiveProperty.bind(null, text)).toThrow(SyntaxError);
13+
});
14+
15+
it('gets first primitive property', () => {
16+
expect(getFirstPrimitiveProperty(`{"swagger":"2.0","test":true}`)).toEqual(['swagger', '2.0']);
17+
expect(getFirstPrimitiveProperty(`{"swagger":2,"baz": {}}`)).toEqual(['swagger', 2]);
18+
expect(getFirstPrimitiveProperty(`{"swagger":true}`)).toEqual(['swagger', true]);
19+
expect(getFirstPrimitiveProperty(`{"swagger":false}`)).toEqual(['swagger', false]);
20+
expect(getFirstPrimitiveProperty(`{"swagger":null}`)).toEqual(['swagger', null]);
21+
expect(getFirstPrimitiveProperty(`{"openapi":"3.0"}`)).toEqual(['openapi', '3.0']);
22+
expect(getFirstPrimitiveProperty(`{"openapi":3.1}`)).toEqual(['openapi', 3.1]);
23+
expect(getFirstPrimitiveProperty(`{"openapi":3.1,"test":"foo"`)).toEqual(['openapi', 3.1]);
24+
});
25+
});

src/getFirstPrimitiveProperty.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { Optional } from '@stoplight/types';
2+
import { createScanner, SyntaxKind } from 'jsonc-parser';
3+
4+
export const getFirstPrimitiveProperty = (text: string): Optional<[string, string | number | boolean | null]> => {
5+
const scanner = createScanner(text, true);
6+
7+
scanner.scan();
8+
9+
if (scanner.getToken() !== SyntaxKind.OpenBraceToken) {
10+
return;
11+
}
12+
13+
scanner.scan();
14+
15+
if (scanner.getToken() === SyntaxKind.CloseBraceToken) {
16+
return;
17+
}
18+
19+
if (scanner.getToken() !== SyntaxKind.StringLiteral) {
20+
throw new SyntaxError('Unexpected character');
21+
}
22+
23+
const property = scanner.getTokenValue();
24+
scanner.scan();
25+
26+
if (scanner.getToken() !== SyntaxKind.ColonToken) {
27+
throw new SyntaxError('Colon expected');
28+
}
29+
30+
scanner.scan();
31+
32+
switch (scanner.getToken()) {
33+
case SyntaxKind.StringLiteral:
34+
return [property, scanner.getTokenValue()];
35+
case SyntaxKind.NumericLiteral:
36+
return [property, Number(scanner.getTokenValue())]; // note we should validate the number, but let's have a loose check
37+
case SyntaxKind.TrueKeyword:
38+
return [property, true];
39+
case SyntaxKind.FalseKeyword:
40+
return [property, false];
41+
case SyntaxKind.NullKeyword:
42+
return [property, null];
43+
case SyntaxKind.Unknown:
44+
throw new SyntaxError('Unexpected character');
45+
case SyntaxKind.EOF:
46+
throw new SyntaxError('Unexpected end of file');
47+
default:
48+
return;
49+
}
50+
};

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export * from './decodePointer';
22
export * from './decodePointerFragment';
33
export * from './encodePointer';
44
export * from './encodePointerFragment';
5+
export * from './getFirstPrimitiveProperty';
56
export * from './getJsonPathForPosition';
67
export * from './getLastPathSegment';
78
export * from './getLocationForJsonPath';

0 commit comments

Comments
 (0)