JSX namespaced name AST (#5231)

* opening element

* closing element
This commit is contained in:
Julian Rosse
2019-07-30 20:58:51 -04:00
committed by Geoffrey Booth
parent 99ad0cc16c
commit 2f27225e8c
4 changed files with 537 additions and 38 deletions

View File

@@ -4,7 +4,7 @@
// nodes are created as the result of actions in the [grammar](grammar.html),
// but some are created by other nodes as a method of code generation. To convert
// the syntax tree into a string of JavaScript code, call `compile()` on the root.
var Access, Arr, Assign, AwaitReturn, Base, Block, BooleanLiteral, Call, Catch, Class, ClassProperty, ClassPrototypeProperty, Code, CodeFragment, ComputedPropertyName, DefaultLiteral, Directive, DynamicImport, DynamicImportCall, Elision, EmptyInterpolation, ExecutableClassBody, Existence, Expansion, ExportAllDeclaration, ExportDeclaration, ExportDefaultDeclaration, ExportNamedDeclaration, ExportSpecifier, ExportSpecifierList, Extends, For, FuncDirectiveReturn, FuncGlyph, HEREGEX_OMIT, HereComment, HoistTarget, IdentifierLiteral, If, ImportClause, ImportDeclaration, ImportDefaultSpecifier, ImportNamespaceSpecifier, ImportSpecifier, ImportSpecifierList, In, Index, InfinityLiteral, Interpolation, JSXAttribute, JSXAttributes, JSXElement, JSXEmptyExpression, JSXExpressionContainer, JSXIdentifier, JSXTag, JSXText, JS_FORBIDDEN, LEADING_BLANK_LINE, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, LineComment, Literal, MetaProperty, ModuleDeclaration, ModuleSpecifier, ModuleSpecifierList, NEGATE, NO, NaNLiteral, NullLiteral, NumberLiteral, Obj, ObjectProperty, Op, Param, Parens, PassthroughLiteral, PropertyName, Range, RegexLiteral, RegexWithInterpolations, Return, Root, SIMPLENUM, SIMPLE_STRING_OMIT, STRING_OMIT, Scope, Sequence, Slice, Splat, StatementLiteral, StringLiteral, StringWithInterpolations, Super, SuperCall, Switch, SwitchCase, SwitchWhen, TAB, THIS, TRAILING_BLANK_LINE, TaggedTemplateCall, TemplateElement, ThisLiteral, Throw, Try, UTILITIES, UndefinedLiteral, Value, While, YES, YieldReturn, addDataToNode, attachCommentsToNode, compact, del, emptyExpressionLocationData, ends, extend, flatten, fragmentsToText, greater, hasLineComments, indentInitial, isAstLocGreater, isFunction, isLiteralArguments, isLiteralThis, isLocationDataEndGreater, isLocationDataStartGreater, isNumber, isPlainObject, isUnassignable, jisonLocationDataToAstLocationData, lesser, locationDataToString, makeDelimitedLiteral, merge, mergeAstLocationData, mergeLocationData, moveComments, multident, replaceUnicodeCodePointEscapes, shouldCacheOrIsAssignable, sniffDirectives, some, starts, throwSyntaxError, unfoldSoak, unshiftAfterComments, utility, zeroWidthLocationDataFromEndLocation,
var Access, Arr, Assign, AwaitReturn, Base, Block, BooleanLiteral, Call, Catch, Class, ClassProperty, ClassPrototypeProperty, Code, CodeFragment, ComputedPropertyName, DefaultLiteral, Directive, DynamicImport, DynamicImportCall, Elision, EmptyInterpolation, ExecutableClassBody, Existence, Expansion, ExportAllDeclaration, ExportDeclaration, ExportDefaultDeclaration, ExportNamedDeclaration, ExportSpecifier, ExportSpecifierList, Extends, For, FuncDirectiveReturn, FuncGlyph, HEREGEX_OMIT, HereComment, HoistTarget, IdentifierLiteral, If, ImportClause, ImportDeclaration, ImportDefaultSpecifier, ImportNamespaceSpecifier, ImportSpecifier, ImportSpecifierList, In, Index, InfinityLiteral, Interpolation, JSXAttribute, JSXAttributes, JSXElement, JSXEmptyExpression, JSXExpressionContainer, JSXIdentifier, JSXNamespacedName, JSXTag, JSXText, JS_FORBIDDEN, LEADING_BLANK_LINE, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, LineComment, Literal, MetaProperty, ModuleDeclaration, ModuleSpecifier, ModuleSpecifierList, NEGATE, NO, NaNLiteral, NullLiteral, NumberLiteral, Obj, ObjectProperty, Op, Param, Parens, PassthroughLiteral, PropertyName, Range, RegexLiteral, RegexWithInterpolations, Return, Root, SIMPLENUM, SIMPLE_STRING_OMIT, STRING_OMIT, Scope, Sequence, Slice, Splat, StatementLiteral, StringLiteral, StringWithInterpolations, Super, SuperCall, Switch, SwitchCase, SwitchWhen, TAB, THIS, TRAILING_BLANK_LINE, TaggedTemplateCall, TemplateElement, ThisLiteral, Throw, Try, UTILITIES, UndefinedLiteral, Value, While, YES, YieldReturn, addDataToNode, attachCommentsToNode, compact, del, emptyExpressionLocationData, ends, extend, extractSameLineLocationDataFirst, extractSameLineLocationDataLast, flatten, fragmentsToText, greater, hasLineComments, indentInitial, isAstLocGreater, isFunction, isLiteralArguments, isLiteralThis, isLocationDataEndGreater, isLocationDataStartGreater, isNumber, isPlainObject, isUnassignable, jisonLocationDataToAstLocationData, lesser, locationDataToString, makeDelimitedLiteral, merge, mergeAstLocationData, mergeLocationData, moveComments, multident, replaceUnicodeCodePointEscapes, shouldCacheOrIsAssignable, sniffDirectives, some, starts, throwSyntaxError, unfoldSoak, unshiftAfterComments, utility, zeroWidthLocationDataFromEndLocation,
indexOf = [].indexOf,
splice = [].splice,
slice1 = [].slice;
@@ -2449,11 +2449,15 @@
return compiledName.concat(this.makeCode('='), val);
}
astProperties() {
var ref1, ref2;
astProperties(o) {
var name, ref1, ref2;
name = this.name;
if (indexOf.call(name.value, ':') >= 0) {
name = new JSXNamespacedName(name);
}
return {
name: this.name.ast(),
value: (ref1 = (ref2 = this.value) != null ? ref2.ast() : void 0) != null ? ref1 : null
name: name.ast(o),
value: (ref1 = (ref2 = this.value) != null ? ref2.ast(o) : void 0) != null ? ref1 : null
};
}
@@ -2550,6 +2554,36 @@
}).call(this);
exports.JSXNamespacedName = JSXNamespacedName = (function() {
class JSXNamespacedName extends Base {
constructor(tag) {
var name, namespace;
super();
[namespace, name] = tag.value.split(':');
this.namespace = new JSXIdentifier(namespace).withLocationDataFrom({
locationData: extractSameLineLocationDataFirst(namespace.length)(tag.locationData)
});
this.name = new JSXIdentifier(name).withLocationDataFrom({
locationData: extractSameLineLocationDataLast(name.length)(tag.locationData)
});
this.locationData = tag.locationData;
}
astProperties(o) {
return {
namespace: this.namespace.ast(o),
name: this.name.ast(o)
};
}
};
JSXNamespacedName.prototype.children = ['namespace', 'name'];
return JSXNamespacedName;
}).call(this);
// Node for a JSX element
exports.JSXElement = JSXElement = (function() {
class JSXElement extends Base {
@@ -2608,10 +2642,18 @@
}
elementAstProperties(o) {
var closingElement, currentExpr, openingElement, rangeDiff, shiftAstLocationData;
var closingElement, columnDiff, currentExpr, openingElement, rangeDiff, ref1, shiftAstLocationData, tagNameAst;
tagNameAst = () => {
var tag;
tag = this.tagName.unwrap();
if ((tag != null ? tag.value : void 0) && indexOf.call(tag.value, ':') >= 0) {
tag = new JSXNamespacedName(tag);
}
return tag.ast(o);
};
openingElement = Object.assign({
type: 'JSXOpeningElement',
name: this.tagName.unwrap().ast(o),
name: tagNameAst(),
selfClosing: this.closingElementLocationData == null,
attributes: this.attributes.ast(o)
}, this.openingElementLocationData);
@@ -2619,32 +2661,38 @@
if (this.closingElementLocationData != null) {
closingElement = Object.assign({
type: 'JSXClosingElement',
name: Object.assign(this.tagName.unwrap().ast(o), jisonLocationDataToAstLocationData(this.tagName.base.closingTagNameLocationData))
name: Object.assign(tagNameAst(), jisonLocationDataToAstLocationData(this.tagName.base.closingTagNameLocationData))
}, this.closingElementLocationData);
if (closingElement.name.type === 'JSXMemberExpression') {
if ((ref1 = closingElement.name.type) === 'JSXMemberExpression' || ref1 === 'JSXNamespacedName') {
rangeDiff = closingElement.range[0] - openingElement.range[0] + '/'.length;
shiftAstLocationData = function(node) {
columnDiff = closingElement.loc.start.column - openingElement.loc.start.column + '/'.length;
shiftAstLocationData = (node) => {
node.range = [node.range[0] + rangeDiff, node.range[1] + rangeDiff];
node.start += rangeDiff;
node.end += rangeDiff;
node.loc.start = {
line: node.loc.start.line,
column: node.loc.start.column + rangeDiff
line: this.closingElementLocationData.loc.start.line,
column: node.loc.start.column + columnDiff
};
return node.loc.end = {
line: node.loc.end.line,
column: node.loc.end.column + rangeDiff
line: this.closingElementLocationData.loc.start.line,
column: node.loc.end.column + columnDiff
};
};
currentExpr = closingElement.name;
while (currentExpr.type === 'JSXMemberExpression') {
if (currentExpr !== closingElement.name) {
shiftAstLocationData(currentExpr);
if (closingElement.name.type === 'JSXMemberExpression') {
currentExpr = closingElement.name;
while (currentExpr.type === 'JSXMemberExpression') {
if (currentExpr !== closingElement.name) {
shiftAstLocationData(currentExpr);
}
shiftAstLocationData(currentExpr.property);
currentExpr = currentExpr.object;
}
shiftAstLocationData(currentExpr.property);
currentExpr = currentExpr.object;
shiftAstLocationData(currentExpr); // JSXNamespacedName
} else {
shiftAstLocationData(closingElement.name.namespace);
shiftAstLocationData(closingElement.name.name);
}
shiftAstLocationData(currentExpr);
}
}
return {openingElement, closingElement};
@@ -8534,6 +8582,44 @@
};
};
extractSameLineLocationDataFirst = function(numChars) {
return function({
range: [startRange],
first_line,
first_column
}) {
return {
first_line,
first_column,
last_line: first_line,
last_column: first_column + numChars - 1,
last_line_exclusive: first_line,
last_column_exclusive: first_column + numChars,
range: [startRange, startRange + numChars]
};
};
};
extractSameLineLocationDataLast = function(numChars) {
return function({
range: [, endRange],
last_line,
last_column,
last_line_exclusive,
last_column_exclusive
}) {
return {
first_line: last_line,
first_column: last_column - (numChars - 1),
last_line: last_line,
last_column: last_column,
last_line_exclusive,
last_column_exclusive,
range: [endRange - numChars, endRange]
};
};
};
// We dont currently have a token corresponding to the empty space
// between interpolation/JSX expression braces, so piece together the location
// data by trimming the braces from the Interpolations location data.

View File

@@ -1649,10 +1649,13 @@ exports.JSXAttribute = class JSXAttribute extends Base
val = @value.compileToFragments o, LEVEL_LIST
compiledName.concat @makeCode('='), val
astProperties: ->
astProperties: (o) ->
name = @name
if ':' in name.value
name = new JSXNamespacedName name
return
name: @name.ast()
value: @value?.ast() ? null
name: name.ast o
value: @value?.ast(o) ? null
exports.JSXAttributes = class JSXAttributes extends Base
constructor: (arr) ->
@@ -1705,6 +1708,21 @@ exports.JSXAttributes = class JSXAttributes extends Base
ast: (o) ->
attribute.ast(o) for attribute in @attributes
exports.JSXNamespacedName = class JSXNamespacedName extends Base
constructor: (tag) ->
super()
[namespace, name] = tag.value.split ':'
@namespace = new JSXIdentifier(namespace).withLocationDataFrom locationData: extractSameLineLocationDataFirst(namespace.length) tag.locationData
@name = new JSXIdentifier(name ).withLocationDataFrom locationData: extractSameLineLocationDataLast(name.length ) tag.locationData
@locationData = tag.locationData
children: ['namespace', 'name']
astProperties: (o) ->
return
namespace: @namespace.ast o
name: @name.ast o
# Node for a JSX element
exports.JSXElement = class JSXElement extends Base
constructor: ({@tagName, @attributes, @content}) ->
@@ -1750,9 +1768,15 @@ exports.JSXElement = class JSXElement extends Base
'JSXElement'
elementAstProperties: (o) ->
tagNameAst = =>
tag = @tagName.unwrap()
if tag?.value and ':' in tag.value
tag = new JSXNamespacedName tag
tag.ast o
openingElement = Object.assign {
type: 'JSXOpeningElement'
name: @tagName.unwrap().ast o
name: tagNameAst()
selfClosing: not @closingElementLocationData?
attributes: @attributes.ast o
}, @openingElementLocationData
@@ -1762,13 +1786,14 @@ exports.JSXElement = class JSXElement extends Base
closingElement = Object.assign {
type: 'JSXClosingElement'
name: Object.assign(
@tagName.unwrap().ast(o),
tagNameAst(),
jisonLocationDataToAstLocationData @tagName.base.closingTagNameLocationData
)
}, @closingElementLocationData
if closingElement.name.type is 'JSXMemberExpression'
if closingElement.name.type in ['JSXMemberExpression', 'JSXNamespacedName']
rangeDiff = closingElement.range[0] - openingElement.range[0] + '/'.length
shiftAstLocationData = (node) ->
columnDiff = closingElement.loc.start.column - openingElement.loc.start.column + '/'.length
shiftAstLocationData = (node) =>
node.range = [
node.range[0] + rangeDiff
node.range[1] + rangeDiff
@@ -1776,17 +1801,21 @@ exports.JSXElement = class JSXElement extends Base
node.start += rangeDiff
node.end += rangeDiff
node.loc.start =
line: node.loc.start.line
column: node.loc.start.column + rangeDiff
line: @closingElementLocationData.loc.start.line
column: node.loc.start.column + columnDiff
node.loc.end =
line: node.loc.end.line
column: node.loc.end.column + rangeDiff
currentExpr = closingElement.name
while currentExpr.type is 'JSXMemberExpression'
shiftAstLocationData currentExpr unless currentExpr is closingElement.name
shiftAstLocationData currentExpr.property
currentExpr = currentExpr.object
shiftAstLocationData currentExpr
line: @closingElementLocationData.loc.start.line
column: node.loc.end.column + columnDiff
if closingElement.name.type is 'JSXMemberExpression'
currentExpr = closingElement.name
while currentExpr.type is 'JSXMemberExpression'
shiftAstLocationData currentExpr unless currentExpr is closingElement.name
shiftAstLocationData currentExpr.property
currentExpr = currentExpr.object
shiftAstLocationData currentExpr
else # JSXNamespacedName
shiftAstLocationData closingElement.name.namespace
shiftAstLocationData closingElement.name.name
{openingElement, closingElement}
@@ -5714,6 +5743,26 @@ zeroWidthLocationDataFromEndLocation = ({range: [, endRange], last_line_exclusiv
range: [endRange, endRange]
}
extractSameLineLocationDataFirst = (numChars) -> ({range: [startRange], first_line, first_column}) -> {
first_line
first_column
last_line: first_line
last_column: first_column + numChars - 1
last_line_exclusive: first_line
last_column_exclusive: first_column + numChars
range: [startRange, startRange + numChars]
}
extractSameLineLocationDataLast = (numChars) -> ({range: [, endRange], last_line, last_column, last_line_exclusive, last_column_exclusive}) -> {
first_line: last_line
first_column: last_column - (numChars - 1)
last_line: last_line
last_column: last_column
last_line_exclusive
last_column_exclusive
range: [endRange - numChars, endRange]
}
# We dont currently have a token corresponding to the empty space
# between interpolation/JSX expression braces, so piece together the location
# data by trimming the braces from the Interpolations location data.

View File

@@ -540,6 +540,58 @@ test "AST as expected for JSXTag node", ->
type: 'JSXEmptyExpression'
]
testExpression '<div:a b:c />',
type: 'JSXElement'
openingElement:
type: 'JSXOpeningElement'
name:
type: 'JSXNamespacedName'
namespace:
type: 'JSXIdentifier'
name: 'div'
name:
type: 'JSXIdentifier'
name: 'a'
attributes: [
type: 'JSXAttribute'
name:
type: 'JSXNamespacedName'
namespace:
type: 'JSXIdentifier'
name: 'b'
name:
type: 'JSXIdentifier'
name: 'c'
]
selfClosing: yes
testExpression '''
<div:a>
{b}
</div:a>
''',
type: 'JSXElement'
openingElement:
type: 'JSXOpeningElement'
name:
type: 'JSXNamespacedName'
namespace:
type: 'JSXIdentifier'
name: 'div'
name:
type: 'JSXIdentifier'
name: 'a'
closingElement:
type: 'JSXClosingElement'
name:
type: 'JSXNamespacedName'
namespace:
type: 'JSXIdentifier'
name: 'div'
name:
type: 'JSXIdentifier'
name: 'a'
test "AST as expected for ComputedPropertyName node", ->
testExpression '[fn]: ->',
type: 'ObjectExpression'

View File

@@ -3258,6 +3258,318 @@ test "AST location data as expected for JSXTag node", ->
line: 1
column: 25
testAstLocationData '<div:a b:c />',
type: 'JSXElement'
openingElement:
name:
namespace:
start: 1
end: 4
range: [1, 4]
loc:
start:
line: 1
column: 1
end:
line: 1
column: 4
name:
start: 5
end: 6
range: [5, 6]
loc:
start:
line: 1
column: 5
end:
line: 1
column: 6
start: 1
end: 6
range: [1, 6]
loc:
start:
line: 1
column: 1
end:
line: 1
column: 6
attributes: [
name:
namespace:
start: 7
end: 8
range: [7, 8]
loc:
start:
line: 1
column: 7
end:
line: 1
column: 8
name:
start: 9
end: 10
range: [9, 10]
loc:
start:
line: 1
column: 9
end:
line: 1
column: 10
start: 7
end: 10
range: [7, 10]
loc:
start:
line: 1
column: 7
end:
line: 1
column: 10
start: 7
end: 10
range: [7, 10]
loc:
start:
line: 1
column: 7
end:
line: 1
column: 10
]
start: 0
end: 13
range: [0, 13]
loc:
start:
line: 1
column: 0
end:
line: 1
column: 13
start: 0
end: 13
range: [0, 13]
loc:
start:
line: 1
column: 0
end:
line: 1
column: 13
testAstLocationData '''
<div:a>
{b}
</div:a>
''',
type: 'JSXElement'
openingElement:
name:
namespace:
start: 1
end: 4
range: [1, 4]
loc:
start:
line: 1
column: 1
end:
line: 1
column: 4
name:
start: 5
end: 6
range: [5, 6]
loc:
start:
line: 1
column: 5
end:
line: 1
column: 6
start: 1
end: 6
range: [1, 6]
loc:
start:
line: 1
column: 1
end:
line: 1
column: 6
start: 0
end: 7
range: [0, 7]
loc:
start:
line: 1
column: 0
end:
line: 1
column: 7
closingElement:
name:
namespace:
start: 16
end: 19
range: [16, 19]
loc:
start:
line: 3
column: 2
end:
line: 3
column: 5
name:
start: 20
end: 21
range: [20, 21]
loc:
start:
line: 3
column: 6
end:
line: 3
column: 7
start: 16
end: 21
range: [16, 21]
loc:
start:
line: 3
column: 2
end:
line: 3
column: 7
start: 14
end: 22
range: [14, 22]
loc:
start:
line: 3
column: 0
end:
line: 3
column: 8
start: 0
end: 22
range: [0, 22]
loc:
start:
line: 1
column: 0
end:
line: 3
column: 8
testAstLocationData '''
<div.a>
{b}
</div.a>
''',
type: 'JSXElement'
openingElement:
name:
object:
start: 1
end: 4
range: [1, 4]
loc:
start:
line: 1
column: 1
end:
line: 1
column: 4
property:
start: 5
end: 6
range: [5, 6]
loc:
start:
line: 1
column: 5
end:
line: 1
column: 6
start: 1
end: 6
range: [1, 6]
loc:
start:
line: 1
column: 1
end:
line: 1
column: 6
start: 0
end: 7
range: [0, 7]
loc:
start:
line: 1
column: 0
end:
line: 1
column: 7
closingElement:
name:
object:
start: 16
end: 19
range: [16, 19]
loc:
start:
line: 3
column: 2
end:
line: 3
column: 5
property:
start: 20
end: 21
range: [20, 21]
loc:
start:
line: 3
column: 6
end:
line: 3
column: 7
start: 16
end: 21
range: [16, 21]
loc:
start:
line: 3
column: 2
end:
line: 3
column: 7
start: 14
end: 22
range: [14, 22]
loc:
start:
line: 3
column: 0
end:
line: 3
column: 8
start: 0
end: 22
range: [0, 22]
loc:
start:
line: 1
column: 0
end:
line: 3
column: 8
test "AST as expected for Try node", ->
testAstLocationData 'try cappuccino',
type: 'TryStatement'