3131//------------------------------------------------------------------------------
3232
3333var ts = require ( "typescript" ) ,
34- assign = require ( "object-assign" ) ;
34+ assign = require ( "object-assign" ) ,
35+ unescape = require ( "lodash.unescape" ) ;
3536
3637//------------------------------------------------------------------------------
3738// Private
@@ -309,7 +310,16 @@ function getTokenType(token) {
309310 case SyntaxKind . NumericLiteral :
310311 return "Numeric" ;
311312
313+ case SyntaxKind . JsxText :
314+ return "JSXText" ;
315+
312316 case SyntaxKind . StringLiteral :
317+ // A TypeScript-StringLiteral token with a TypeScript-JsxAttribute or TypeScript-JsxElement parent,
318+ // must actually be an ESTree-JSXText token
319+ if ( token . parent && ( token . parent . kind === SyntaxKind . JsxAttribute || token . parent . kind === SyntaxKind . JsxElement ) ) {
320+ return "JSXText" ;
321+ }
322+
313323 return "String" ;
314324
315325 case SyntaxKind . RegularExpressionLiteral :
@@ -323,6 +333,23 @@ function getTokenType(token) {
323333 default :
324334 }
325335
336+ // Some JSX tokens have to be determined based on their parent
337+ if ( token . parent ) {
338+ if ( token . kind === SyntaxKind . Identifier && token . parent . kind === SyntaxKind . FirstNode ) {
339+ return "JSXIdentifier" ;
340+ }
341+
342+ if ( token . parent . kind >= SyntaxKind . JsxElement && token . parent . kind <= SyntaxKind . JsxAttribute ) {
343+ if ( token . kind === SyntaxKind . FirstNode ) {
344+ return "JSXMemberExpression" ;
345+ }
346+
347+ if ( token . kind === SyntaxKind . Identifier ) {
348+ return "JSXIdentifier" ;
349+ }
350+ }
351+ }
352+
326353 return "Identifier" ;
327354}
328355
@@ -368,7 +395,6 @@ function convertTokens(ast) {
368395 if ( converted ) {
369396 result . push ( converted ) ;
370397 }
371-
372398 token = ts . findNextToken ( token , ast ) ;
373399 }
374400
@@ -424,6 +450,38 @@ module.exports = function(ast, extra) {
424450 return convert ( child , node ) ;
425451 }
426452
453+ /**
454+ * Converts a TypeScript JSX node.tagName into an ESTree node.name
455+ * @param {Object } tagName the tagName object from a JSX TSNode
456+ * @param {Object } ast the AST object
457+ * @returns {Object } the converted ESTree name object
458+ */
459+ function convertTypeScriptJSXTagNameToESTreeName ( tagName ) {
460+ var tagNameToken = convertToken ( tagName , ast ) ;
461+
462+ if ( tagNameToken . type === "JSXMemberExpression" ) {
463+
464+ var isNestedMemberExpression = ( node . tagName . left . kind === SyntaxKind . FirstNode ) ;
465+
466+ // Convert TSNode left and right objects into ESTreeNode object
467+ // and property objects
468+ tagNameToken . object = convertChild ( node . tagName . left ) ;
469+ tagNameToken . property = convertChild ( node . tagName . right ) ;
470+
471+ // Assign the appropriate types
472+ tagNameToken . object . type = ( isNestedMemberExpression ) ? "JSXMemberExpression" : "JSXIdentifier" ;
473+ tagNameToken . property . type = "JSXIdentifier" ;
474+
475+ } else {
476+
477+ tagNameToken . name = tagNameToken . value ;
478+ }
479+
480+ delete tagNameToken . value ;
481+
482+ return tagNameToken ;
483+ }
484+
427485 switch ( node . kind ) {
428486 case SyntaxKind . SourceFile :
429487 assign ( result , {
@@ -1318,7 +1376,7 @@ module.exports = function(ast, extra) {
13181376 case SyntaxKind . StringLiteral :
13191377 assign ( result , {
13201378 type : "Literal" ,
1321- value : node . text ,
1379+ value : unescape ( node . text ) ,
13221380 raw : ast . text . slice ( result . range [ 0 ] , result . range [ 1 ] )
13231381 } ) ;
13241382 break ;
@@ -1369,13 +1427,132 @@ module.exports = function(ast, extra) {
13691427 simplyCopy ( ) ;
13701428 break ;
13711429
1430+ // JSX
1431+
1432+ case SyntaxKind . JsxElement :
1433+ assign ( result , {
1434+ type : "JSXElement" ,
1435+ openingElement : convertChild ( node . openingElement ) ,
1436+ closingElement : convertChild ( node . closingElement ) ,
1437+ children : node . children . map ( convertChild )
1438+ } ) ;
1439+
1440+ break ;
1441+
1442+ case SyntaxKind . JsxSelfClosingElement :
1443+ // Convert SyntaxKind.JsxSelfClosingElement to SyntaxKind.JsxOpeningElement,
1444+ // TypeScript does not seem to have the idea of openingElement when tag is self-closing
1445+ node . kind = SyntaxKind . JsxOpeningElement ;
1446+ assign ( result , {
1447+ type : "JSXElement" ,
1448+ openingElement : convertChild ( node ) ,
1449+ closingElement : null ,
1450+ children : [ ]
1451+ } ) ;
1452+
1453+ break ;
1454+
1455+ case SyntaxKind . JsxOpeningElement :
1456+ var openingTagName = convertTypeScriptJSXTagNameToESTreeName ( node . tagName ) ;
1457+ assign ( result , {
1458+ type : "JSXOpeningElement" ,
1459+ selfClosing : ! ( node . parent && node . parent . closingElement ) ,
1460+ name : openingTagName ,
1461+ attributes : node . attributes . map ( convertChild )
1462+ } ) ;
1463+
1464+ break ;
1465+
1466+ case SyntaxKind . JsxClosingElement :
1467+ var closingTagName = convertTypeScriptJSXTagNameToESTreeName ( node . tagName ) ;
1468+ assign ( result , {
1469+ type : "JSXClosingElement" ,
1470+ name : closingTagName
1471+ } ) ;
1472+
1473+ break ;
1474+
1475+ case SyntaxKind . JsxExpression :
1476+ var eloc = ast . getLineAndCharacterOfPosition ( result . range [ 0 ] + 1 ) ;
1477+ var expression = ( node . expression ) ? convertChild ( node . expression ) : {
1478+ type : "JSXEmptyExpression" ,
1479+ loc : {
1480+ start : {
1481+ line : eloc . line + 1 ,
1482+ column : eloc . character
1483+ } ,
1484+ end : {
1485+ line : result . loc . end . line ,
1486+ column : result . loc . end . column - 1
1487+ }
1488+ } ,
1489+ range : [ result . range [ 0 ] + 1 , result . range [ 1 ] - 1 ]
1490+ } ;
1491+
1492+ assign ( result , {
1493+ type : "JSXExpressionContainer" ,
1494+ expression : expression
1495+ } ) ;
1496+
1497+ break ;
1498+
1499+ case SyntaxKind . JsxAttribute :
1500+ var attributeName = convertToken ( node . name , ast ) ;
1501+ attributeName . name = attributeName . value ;
1502+ delete attributeName . value ;
1503+
1504+ assign ( result , {
1505+ type : "JSXAttribute" ,
1506+ name : attributeName ,
1507+ value : convertChild ( node . initializer )
1508+ } ) ;
1509+
1510+ break ;
1511+
1512+ case SyntaxKind . JsxText :
1513+ assign ( result , {
1514+ type : "Literal" ,
1515+ value : ast . text . slice ( node . pos , node . end ) ,
1516+ raw : ast . text . slice ( node . pos , node . end )
1517+ } ) ;
1518+
1519+ result . loc . start . column = node . pos ;
1520+ result . range [ 0 ] = node . pos ;
1521+
1522+ break ;
1523+
1524+ case SyntaxKind . JsxSpreadAttribute :
1525+ assign ( result , {
1526+ type : "JSXSpreadAttribute" ,
1527+ argument : convertChild ( node . expression )
1528+ } ) ;
1529+
1530+ break ;
1531+
1532+ case SyntaxKind . FirstNode :
1533+ var jsxMemberExpressionObject = convertChild ( node . left ) ;
1534+ jsxMemberExpressionObject . type = "JSXIdentifier" ;
1535+ delete jsxMemberExpressionObject . value ;
1536+
1537+ var jsxMemberExpressionProperty = convertChild ( node . right ) ;
1538+ jsxMemberExpressionProperty . type = "JSXIdentifier" ;
1539+ delete jsxMemberExpressionObject . value ;
1540+
1541+ assign ( result , {
1542+ type : "JSXMemberExpression" ,
1543+ object : jsxMemberExpressionObject ,
1544+ property : jsxMemberExpressionProperty
1545+ } ) ;
1546+
1547+ break ;
1548+
13721549 // TypeScript specific
13731550
13741551 case SyntaxKind . ParenthesizedExpression :
13751552 return convert ( node . expression , parent ) ;
13761553
13771554 default :
1378- console . log ( node . kind ) ;
1555+ console . log ( "unsupported node.kind:" , node . kind ) ;
13791556 result = null ;
13801557 }
13811558
0 commit comments