Skip to content
This repository was archived by the owner on Jan 19, 2019. It is now read-only.

Commit 5b49870

Browse files
authored
Fix: Location data for typeAnnotations (#378)
1 parent 62088b1 commit 5b49870

File tree

8 files changed

+800
-667
lines changed

8 files changed

+800
-667
lines changed

lib/convert.js

Lines changed: 48 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,12 @@ module.exports = function convert(config) {
8585
*/
8686
function convertTypeAnnotation(child) {
8787
const annotation = convertChild(child);
88+
const annotationStartCol = child.getFullStart() - 1;
89+
const loc = nodeUtils.getLocFor(annotationStartCol, child.end, ast);
8890
return {
8991
type: AST_NODE_TYPES.TypeAnnotation,
90-
loc: annotation.loc,
91-
range: annotation.range,
92+
loc,
93+
range: [annotationStartCol, child.end],
9294
typeAnnotation: annotation
9395
};
9496
}
@@ -160,7 +162,7 @@ module.exports = function convert(config) {
160162

161163
const constraint = typeParameter.constraint
162164
? convert({ node: typeParameter.constraint, parent: typeParameter, ast, additionalOptions })
163-
: null;
165+
: undefined;
164166

165167
const defaultParameter = typeParameter.default
166168
? convert({ node: typeParameter.default, parent: typeParameter, ast, additionalOptions })
@@ -279,7 +281,7 @@ module.exports = function convert(config) {
279281
result.type = customType;
280282
Object
281283
.keys(node)
282-
.filter(key => !(/^(?:kind|parent|pos|end|flags|modifierFlagsCache|jsDoc)$/.test(key)))
284+
.filter(key => !(/^(?:_children|kind|parent|pos|end|flags|modifierFlagsCache|jsDoc)$/.test(key)))
283285
.forEach(key => {
284286
if (key === "type") {
285287
result.typeAnnotation = (node.type) ? convertTypeAnnotation(node.type) : null;
@@ -392,6 +394,19 @@ module.exports = function convert(config) {
392394
result.modifiers = remainingModifiers.map(convertChild);
393395
}
394396

397+
/**
398+
* Uses the current TSNode's end location for its `type` to adjust the location data of the given
399+
* ESTreeNode, which should be the parent of the final typeAnnotation node
400+
* @param {ESTreeNode} typeAnnotationParent The node that will have its location data mutated
401+
* @returns {void}
402+
*/
403+
function fixTypeAnnotationParentLocation(typeAnnotationParent) {
404+
const end = node.type.getEnd();
405+
typeAnnotationParent.range[1] = end;
406+
const loc = nodeUtils.getLocFor(typeAnnotationParent.range[0], typeAnnotationParent.range[1], ast);
407+
typeAnnotationParent.loc = loc;
408+
}
409+
395410
/**
396411
* The core of the conversion logic:
397412
* Identify and convert each relevant TypeScript SyntaxKind
@@ -619,15 +634,7 @@ module.exports = function convert(config) {
619634

620635
if (node.type) {
621636
result.id.typeAnnotation = convertTypeAnnotation(node.type);
622-
result.id.range[1] = node.type.getEnd();
623-
624-
const identifierEnd = node.name.getEnd();
625-
const numCharsBetweenTypeAndIdentifier = node.type.getStart() - (node.type.getFullStart() - identifierEnd - ":".length) - identifierEnd;
626-
627-
result.id.typeAnnotation.range = [
628-
result.id.typeAnnotation.range[0] - numCharsBetweenTypeAndIdentifier,
629-
result.id.typeAnnotation.range[1]
630-
];
637+
fixTypeAnnotationParentLocation(result.id);
631638
}
632639
break;
633640
}
@@ -704,7 +711,7 @@ module.exports = function convert(config) {
704711
node,
705712
parentNode =>
706713
(parentNode.kind === SyntaxKind.BinaryExpression || parentNode.kind === SyntaxKind.ArrowFunction)
707-
);
714+
);
708715
const objectAssignNode = (
709716
ancestorNode &&
710717
ancestorNode.kind === SyntaxKind.BinaryExpression &&
@@ -806,7 +813,7 @@ module.exports = function convert(config) {
806813
value: convertChild(node.initializer),
807814
computed: nodeUtils.isComputedProperty(node.name),
808815
static: nodeUtils.hasStaticModifierFlag(node),
809-
readonly: nodeUtils.hasModifier(SyntaxKind.ReadonlyKeyword, node)
816+
readonly: nodeUtils.hasModifier(SyntaxKind.ReadonlyKeyword, node) || undefined
810817
});
811818

812819
if (node.type) {
@@ -836,7 +843,12 @@ module.exports = function convert(config) {
836843
case SyntaxKind.SetAccessor:
837844
case SyntaxKind.MethodDeclaration: {
838845

839-
const openingParen = nodeUtils.findNextToken(node.name, ast);
846+
const openingParen = nodeUtils.findFirstMatchingToken(node.name, ast, token => {
847+
if (!token || !token.kind) {
848+
return false;
849+
}
850+
return nodeUtils.getTextForTokenKind(token.kind) === "(";
851+
});
840852

841853
const methodLoc = ast.getLineAndCharacterOfPosition(openingParen.getStart()),
842854
nodeIsMethod = (node.kind === SyntaxKind.MethodDeclaration),
@@ -1273,7 +1285,7 @@ module.exports = function convert(config) {
12731285

12741286
if (node.type) {
12751287
parameter.typeAnnotation = convertTypeAnnotation(node.type);
1276-
parameter.range[1] = node.type.getEnd();
1288+
fixTypeAnnotationParentLocation(parameter);
12771289
}
12781290

12791291
if (node.questionToken) {
@@ -1285,10 +1297,10 @@ module.exports = function convert(config) {
12851297
type: AST_NODE_TYPES.TSParameterProperty,
12861298
range: [node.getStart(), node.end],
12871299
loc: nodeUtils.getLoc(node, ast),
1288-
accessibility: nodeUtils.getTSNodeAccessibility(node),
1289-
readonly: nodeUtils.hasModifier(SyntaxKind.ReadonlyKeyword, node),
1290-
static: nodeUtils.hasModifier(SyntaxKind.StaticKeyword, node),
1291-
export: nodeUtils.hasModifier(SyntaxKind.ExportKeyword, node),
1300+
accessibility: nodeUtils.getTSNodeAccessibility(node) || undefined,
1301+
readonly: nodeUtils.hasModifier(SyntaxKind.ReadonlyKeyword, node) || undefined,
1302+
static: nodeUtils.hasModifier(SyntaxKind.StaticKeyword, node) || undefined,
1303+
export: nodeUtils.hasModifier(SyntaxKind.ExportKeyword, node) || undefined,
12921304
parameter: result
12931305
};
12941306
}
@@ -1941,9 +1953,9 @@ module.exports = function convert(config) {
19411953
key: convertChild(node.name),
19421954
params: convertParameters(node.parameters),
19431955
typeAnnotation: (node.type) ? convertTypeAnnotation(node.type) : null,
1944-
readonly: nodeUtils.hasModifier(SyntaxKind.ReadonlyKeyword, node),
1956+
readonly: nodeUtils.hasModifier(SyntaxKind.ReadonlyKeyword, node) || undefined,
19451957
static: nodeUtils.hasModifier(SyntaxKind.StaticKeyword, node),
1946-
export: nodeUtils.hasModifier(SyntaxKind.ExportKeyword, node)
1958+
export: nodeUtils.hasModifier(SyntaxKind.ExportKeyword, node) || undefined
19471959
});
19481960

19491961
const accessibility = nodeUtils.getTSNodeAccessibility(node);
@@ -1961,14 +1973,14 @@ module.exports = function convert(config) {
19611973
case SyntaxKind.PropertySignature: {
19621974
Object.assign(result, {
19631975
type: AST_NODE_TYPES.TSPropertySignature,
1964-
optional: nodeUtils.isOptional(node),
1976+
optional: nodeUtils.isOptional(node) || undefined,
19651977
computed: nodeUtils.isComputedProperty(node.name),
19661978
key: convertChild(node.name),
1967-
typeAnnotation: (node.type) ? convertTypeAnnotation(node.type) : null,
1968-
initializer: convertChild(node.initializer),
1969-
readonly: nodeUtils.hasModifier(SyntaxKind.ReadonlyKeyword, node),
1970-
static: nodeUtils.hasModifier(SyntaxKind.StaticKeyword, node),
1971-
export: nodeUtils.hasModifier(SyntaxKind.ExportKeyword, node)
1979+
typeAnnotation: (node.type) ? convertTypeAnnotation(node.type) : undefined,
1980+
initializer: convertChild(node.initializer) || undefined,
1981+
readonly: nodeUtils.hasModifier(SyntaxKind.ReadonlyKeyword, node) || undefined,
1982+
static: nodeUtils.hasModifier(SyntaxKind.StaticKeyword, node) || undefined,
1983+
export: nodeUtils.hasModifier(SyntaxKind.ExportKeyword, node) || undefined
19721984
});
19731985

19741986
const accessibility = nodeUtils.getTSNodeAccessibility(node);
@@ -1984,9 +1996,9 @@ module.exports = function convert(config) {
19841996
type: AST_NODE_TYPES.TSIndexSignature,
19851997
index: convertChild(node.parameters[0]),
19861998
typeAnnotation: (node.type) ? convertTypeAnnotation(node.type) : null,
1987-
readonly: nodeUtils.hasModifier(SyntaxKind.ReadonlyKeyword, node),
1999+
readonly: nodeUtils.hasModifier(SyntaxKind.ReadonlyKeyword, node) || undefined,
19882000
static: nodeUtils.hasModifier(SyntaxKind.StaticKeyword, node),
1989-
export: nodeUtils.hasModifier(SyntaxKind.ExportKeyword, node)
2001+
export: nodeUtils.hasModifier(SyntaxKind.ExportKeyword, node) || undefined
19902002
});
19912003

19922004
const accessibility = nodeUtils.getTSNodeAccessibility(node);
@@ -2057,6 +2069,11 @@ module.exports = function convert(config) {
20572069
parameterName: convertChild(node.parameterName),
20582070
typeAnnotation: convertTypeAnnotation(node.type)
20592071
});
2072+
/**
2073+
* Specific fix for type-guard location data
2074+
*/
2075+
result.typeAnnotation.loc = result.typeAnnotation.typeAnnotation.loc;
2076+
result.typeAnnotation.range = result.typeAnnotation.typeAnnotation.range;
20602077
break;
20612078

20622079
case SyntaxKind.EnumDeclaration: {

lib/node-utils.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ module.exports = {
170170
getTSNodeAccessibility,
171171
hasStaticModifierFlag,
172172
findNextToken,
173+
findFirstMatchingToken,
173174
findChildOfKind,
174175
findFirstMatchingAncestor,
175176
findAncestorOfKind,
@@ -413,6 +414,23 @@ function findNextToken(previousToken, parent) {
413414
return ts.findNextToken(previousToken, parent);
414415
}
415416

417+
/**
418+
* Find the first matching token based on the given predicate function.
419+
* @param {TSToken} previousToken The previous TSToken
420+
* @param {TSNode} parent The parent TSNode
421+
* @param {Function} predicate The predicate function to apply to each checked token
422+
* @returns {TSToken|undefined} a matching TSToken
423+
*/
424+
function findFirstMatchingToken(previousToken, parent, predicate) {
425+
while (previousToken) {
426+
if (predicate(previousToken)) {
427+
return previousToken;
428+
}
429+
previousToken = findNextToken(previousToken, parent);
430+
}
431+
return undefined;
432+
}
433+
416434
/**
417435
* Finds the first child TSNode which matches the given kind
418436
* @param {TSNode} node The parent TSNode

package.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,17 @@
1818
},
1919
"license": "BSD-2-Clause",
2020
"devDependencies": {
21-
"babel-code-frame": "^6.22.0",
22-
"babylon": "^7.0.0-beta.20",
23-
"eslint": "3.19.0",
21+
"babel-code-frame": "^6.26.0",
22+
"babylon": "^7.0.0-beta.22",
23+
"eslint": "4.6.1",
2424
"eslint-config-eslint": "4.0.0",
25-
"eslint-plugin-node": "4.2.2",
25+
"eslint-plugin-node": "5.1.1",
2626
"eslint-release": "0.10.3",
2727
"glob": "^7.1.2",
28-
"jest": "20.0.4",
28+
"jest": "21.0.1",
2929
"lodash.isplainobject": "^4.0.6",
3030
"npm-license": "0.3.3",
31-
"shelljs": "0.7.7",
31+
"shelljs": "0.7.8",
3232
"shelljs-nodecli": "0.1.1",
3333
"typescript": "~2.4.0"
3434
},
@@ -54,7 +54,7 @@
5454
},
5555
"dependencies": {
5656
"lodash.unescape": "4.0.1",
57-
"semver": "5.3.0"
57+
"semver": "5.4.1"
5858
},
5959
"peerDependencies": {
6060
"typescript": "*"

tests/ast-alignment/parse.js

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,11 @@ function parseWithBabylonPluginTypescript(text, parserOptions) { // eslint-disab
2424
plugins: [
2525
"jsx",
2626
"typescript",
27-
"doExpressions",
2827
"objectRestSpread",
2928
"decorators",
3029
"classProperties",
31-
"exportExtensions",
3230
"asyncGenerators",
33-
"functionBind",
34-
"functionSent",
3531
"dynamicImport",
36-
"numericSeparator",
3732
"estree"
3833
]
3934
}, parserOptions));

0 commit comments

Comments
 (0)