ast for jsx content (#5187)

This commit is contained in:
Julian Rosse 2019-03-31 13:24:58 -04:00 committed by Geoffrey Booth
parent 730a4bcdad
commit 0c2d3673d3
4 changed files with 493 additions and 25 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, CSXAttribute, CSXAttributes, CSXElement, CSXExpressionContainer, CSXIdentifier, CSXTag, Call, Catch, Class, Code, CodeFragment, ComputedPropertyName, DefaultLiteral, DynamicImport, DynamicImportCall, Elision, 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, 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, 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, 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, some, starts, throwSyntaxError, unfoldSoak, unshiftAfterComments, utility,
var Access, Arr, Assign, AwaitReturn, Base, Block, BooleanLiteral, CSXAttribute, CSXAttributes, CSXElement, CSXEmptyExpression, CSXExpressionContainer, CSXIdentifier, CSXTag, CSXText, Call, Catch, Class, Code, CodeFragment, ComputedPropertyName, DefaultLiteral, DynamicImport, DynamicImportCall, Elision, 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, 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, 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, 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, some, starts, throwSyntaxError, unfoldSoak, unshiftAfterComments, utility,
indexOf = [].indexOf,
splice = [].splice,
slice1 = [].slice;
@ -2152,9 +2152,13 @@
// Comment delimited by `###` (becoming `/* */`).
exports.HereComment = HereComment = class HereComment extends Base {
constructor({content, newLine, unshift}) {
constructor({
content: content1,
newLine,
unshift
}) {
super();
this.content = content;
this.content = content1;
this.newLine = newLine;
this.unshift = unshift;
}
@ -2195,9 +2199,13 @@
// Comment running from `#` to the end of a line (becoming `//`).
exports.LineComment = LineComment = class LineComment extends Base {
constructor({content, newLine, unshift}) {
constructor({
content: content1,
newLine,
unshift
}) {
super();
this.content = content;
this.content = content1;
this.newLine = newLine;
this.unshift = unshift;
}
@ -2225,11 +2233,11 @@
exports.CSXExpressionContainer = CSXExpressionContainer = (function() {
class CSXExpressionContainer extends Base {
constructor(expression1) {
constructor(expression1, {locationData} = {}) {
super();
this.expression = expression1;
this.expression.csxAttribute = true;
this.locationData = this.expression.locationData;
this.locationData = locationData != null ? locationData : this.expression.locationData;
}
compileNode(o) {
@ -2254,6 +2262,35 @@
}).call(this);
exports.CSXEmptyExpression = CSXEmptyExpression = class CSXEmptyExpression extends Base {
astType() {
return 'JSXEmptyExpression';
}
};
exports.CSXText = CSXText = class CSXText extends Base {
constructor(stringLiteral) {
super();
this.value = stringLiteral.unquote(true, true);
this.locationData = stringLiteral.locationData;
}
astType() {
return 'JSXText';
}
astProperties() {
return {
value: this.value,
extra: {
raw: this.value
}
};
}
};
exports.CSXAttribute = CSXAttribute = (function() {
class CSXAttribute extends Base {
constructor({
@ -2390,12 +2427,12 @@
constructor({
tagName: tagName1,
attributes,
content
content: content1
}) {
super();
this.tagName = tagName1;
this.attributes = attributes;
this.content = content;
this.content = content1;
}
compileNode(o) {
@ -2495,9 +2532,57 @@
return {openingFragment, closingFragment};
}
contentAst(o) {
var base1, child, children, content, element, expression, j, len1, results, unwrapped;
if (!(this.content && !(typeof (base1 = this.content.base).isEmpty === "function" ? base1.isEmpty() : void 0))) {
return [];
}
content = this.content.unwrapAll();
children = (function() {
var j, len1, ref1, results;
if (content instanceof StringLiteral) {
return [new CSXText(content)]; // StringWithInterpolations
} else {
ref1 = this.content.unwrapAll().extractElements(o, {
includeInterpolationWrappers: true
});
results = [];
for (j = 0, len1 = ref1.length; j < len1; j++) {
element = ref1[j];
if (element instanceof StringLiteral) {
results.push(new CSXText(element)); // Interpolation
} else {
({expression} = element);
if (expression == null) {
results.push(new CSXEmptyExpression().withLocationDataFrom(element));
} else {
unwrapped = expression.unwrapAll();
if (unwrapped instanceof CSXElement) {
results.push(unwrapped);
} else {
results.push(new CSXExpressionContainer(unwrapped, {
locationData: element.locationData
}));
}
}
}
}
return results;
}
}).call(this);
results = [];
for (j = 0, len1 = children.length; j < len1; j++) {
child = children[j];
if (!(child instanceof CSXText && child.value.length === 0)) {
results.push(child.ast(o));
}
}
return results;
}
astProperties(o) {
return Object.assign(this.isFragment() ? this.fragmentAstProperties(o) : this.elementAstProperties(o), {
children: []
children: this.contentAst(o)
});
}
@ -6754,14 +6839,14 @@
return this.body.shouldCache();
}
extractElements(o) {
extractElements(o, {includeInterpolationWrappers} = {}) {
var elements, expr, salvagedComments;
// Assumes that `expr` is `Block`
expr = this.body.unwrap();
elements = [];
salvagedComments = [];
expr.traverseChildren(false, (node) => {
var base1, comment, commentPlaceholder, j, k, len1, len2, ref1, ref2, unwrapped;
var comment, commentPlaceholder, j, k, len1, len2, ref1, ref2, ref3, unwrapped;
if (node instanceof StringLiteral) {
if (node.comments) {
salvagedComments.push(...node.comments);
@ -6785,19 +6870,19 @@
(commentPlaceholder.comments != null ? commentPlaceholder.comments : commentPlaceholder.comments = []).push(...node.comments);
}
elements.push(new Value(commentPlaceholder));
} else if (node.expression) {
} else if (node.expression || includeInterpolationWrappers) {
if (node.comments) {
((base1 = node.expression).comments != null ? base1.comments : base1.comments = []).push(...node.comments);
((ref2 = node.expression) != null ? ref2.comments != null ? ref2.comments : ref2.comments = [] : void 0).push(...node.comments);
}
elements.push(node.expression);
elements.push(includeInterpolationWrappers ? node : node.expression);
}
return false;
} else if (node.comments) {
// This node is getting discarded, but salvage its comments.
if (elements.length !== 0 && !(elements[elements.length - 1] instanceof StringLiteral)) {
ref2 = node.comments;
for (k = 0, len2 = ref2.length; k < len2; k++) {
comment = ref2[k];
ref3 = node.comments;
for (k = 0, len2 = ref3.length; k < len2; k++) {
comment = ref3[k];
comment.unshift = false;
comment.newLine = true;
}

View File

@ -1503,10 +1503,10 @@ exports.CSXIdentifier = class CSXIdentifier extends IdentifierLiteral
astType: -> 'JSXIdentifier'
exports.CSXExpressionContainer = class CSXExpressionContainer extends Base
constructor: (@expression) ->
constructor: (@expression, {locationData} = {}) ->
super()
@expression.csxAttribute = yes
@locationData = @expression.locationData
@locationData = locationData ? @expression.locationData
children: ['expression']
@ -1519,6 +1519,24 @@ exports.CSXExpressionContainer = class CSXExpressionContainer extends Base
return
expression: @expression.ast()
exports.CSXEmptyExpression = class CSXEmptyExpression extends Base
astType: -> 'JSXEmptyExpression'
exports.CSXText = class CSXText extends Base
constructor: (stringLiteral) ->
super()
@value = stringLiteral.unquote yes, yes
@locationData = stringLiteral.locationData
astType: -> 'JSXText'
astProperties: ->
return {
@value
extra:
raw: @value
}
exports.CSXAttribute = class CSXAttribute extends Base
constructor: ({@name, value}) ->
super()
@ -1695,6 +1713,30 @@ exports.CSXElement = class CSXElement extends Base
{openingFragment, closingFragment}
contentAst: (o) ->
return [] unless @content and not @content.base.isEmpty?()
content = @content.unwrapAll()
children =
if content instanceof StringLiteral
[new CSXText content]
else # StringWithInterpolations
for element in @content.unwrapAll().extractElements o, includeInterpolationWrappers: yes
if element instanceof StringLiteral
new CSXText element
else # Interpolation
{expression} = element
unless expression?
new CSXEmptyExpression().withLocationDataFrom element
else
unwrapped = expression.unwrapAll()
if unwrapped instanceof CSXElement
unwrapped
else
new CSXExpressionContainer unwrapped, locationData: element.locationData
child.ast(o) for child in children when not (child instanceof CSXText and child.value.length is 0)
astProperties: (o) ->
Object.assign(
if @isFragment()
@ -1702,7 +1744,7 @@ exports.CSXElement = class CSXElement extends Base
else
@elementAstProperties o
,
children: []
children: @contentAst o
)
astLocationData: ->
@ -4519,7 +4561,7 @@ exports.StringWithInterpolations = class StringWithInterpolations extends Base
shouldCache: -> @body.shouldCache()
extractElements: (o) ->
extractElements: (o, {includeInterpolationWrappers} = {}) ->
# Assumes that `expr` is `Block`
expr = @body.unwrap()
@ -4543,9 +4585,9 @@ exports.StringWithInterpolations = class StringWithInterpolations extends Base
commentPlaceholder.comments = unwrapped.comments
(commentPlaceholder.comments ?= []).push node.comments... if node.comments
elements.push new Value commentPlaceholder
else if node.expression
(node.expression.comments ?= []).push node.comments... if node.comments
elements.push node.expression
else if node.expression or includeInterpolationWrappers
(node.expression?.comments ?= []).push node.comments... if node.comments
elements.push if includeInterpolationWrappers then node else node.expression
return no
else if node.comments
# This node is getting discarded, but salvage its comments.

View File

@ -343,6 +343,91 @@ test "AST as expected for CSXTag node", ->
postfix: yes
]
testExpression '<div>abc</div>',
type: 'JSXElement'
openingElement:
type: 'JSXOpeningElement'
name:
type: 'JSXIdentifier'
name: 'div'
attributes: []
selfClosing: no
closingElement:
type: 'JSXClosingElement'
name:
type: 'JSXIdentifier'
name: 'div'
children: [
type: 'JSXText'
extra:
raw: 'abc'
value: 'abc'
]
testExpression '''
<a>
{b}
<c />
</a>
''',
type: 'JSXElement'
openingElement:
type: 'JSXOpeningElement'
name:
type: 'JSXIdentifier'
name: 'a'
attributes: []
selfClosing: no
closingElement:
type: 'JSXClosingElement'
name:
type: 'JSXIdentifier'
name: 'a'
children: [
type: 'JSXText'
extra:
raw: '\n '
value: '\n '
,
type: 'JSXExpressionContainer'
expression: ID 'b'
,
type: 'JSXText'
extra:
raw: '\n '
value: '\n '
,
type: 'JSXElement'
openingElement:
type: 'JSXOpeningElement'
name:
type: 'JSXIdentifier'
name: 'c'
selfClosing: true
closingElement: null
children: []
,
type: 'JSXText'
extra:
raw: '\n'
value: '\n'
]
testExpression '<>abc{}</>',
type: 'JSXFragment'
openingFragment:
type: 'JSXOpeningFragment'
closingFragment:
type: 'JSXClosingFragment'
children: [
type: 'JSXText'
extra:
raw: 'abc'
value: 'abc'
,
type: 'JSXEmptyExpression'
]
# test "AST as expected for PropertyName node", ->
# testExpression 'Object.assign',
# properties: [

View File

@ -2762,6 +2762,262 @@ test "AST location data as expected for CSXTag node", ->
column: 11
]
testAstLocationData '<div>abc</div>',
type: 'JSXElement'
openingElement:
name:
start: 1
end: 4
range: [1, 4]
loc:
start:
line: 1
column: 1
end:
line: 1
column: 4
start: 0
end: 5
range: [0, 5]
loc:
start:
line: 1
column: 0
end:
line: 1
column: 5
closingElement:
name:
start: 10
end: 13
range: [10, 13]
loc:
start:
line: 1
column: 10
end:
line: 1
column: 13
start: 8
end: 14
range: [8, 14]
loc:
start:
line: 1
column: 8
end:
line: 1
column: 14
children: [
start: 5
end: 8
range: [5, 8]
loc:
start:
line: 1
column: 5
end:
line: 1
column: 8
]
start: 0
end: 14
range: [0, 14]
loc:
start:
line: 1
column: 0
end:
line: 1
column: 14
testAstLocationData '''
<a>
{b}
<c />
</a>
''',
type: 'JSXElement'
openingElement:
name:
start: 1
end: 2
range: [1, 2]
loc:
start:
line: 1
column: 1
end:
line: 1
column: 2
start: 0
end: 3
range: [0, 3]
loc:
start:
line: 1
column: 0
end:
line: 1
column: 3
closingElement:
name:
start: 20
end: 21
range: [20, 21]
loc:
start:
line: 4
column: 2
end:
line: 4
column: 3
start: 18
end: 22
range: [18, 22]
loc:
start:
line: 4
column: 0
end:
line: 4
column: 4
children: [
start: 3
end: 6
range: [3, 6]
loc:
start:
line: 1
column: 3
end:
line: 2
column: 2
,
expression:
start: 7
end: 8
range: [7, 8]
loc:
start:
line: 2
column: 3
end:
line: 2
column: 4
start: 6
end: 9
range: [6, 9]
loc:
start:
line: 2
column: 2
end:
line: 2
column: 5
,
start: 9
end: 12
range: [9, 12]
loc:
start:
line: 2
column: 5
end:
line: 3
column: 2
,
start: 12
end: 17
range: [12, 17]
loc:
start:
line: 3
column: 2
end:
line: 3
column: 7
,
start: 17
end: 18
range: [17, 18]
loc:
start:
line: 3
column: 7
end:
line: 4
column: 0
]
start: 0
end: 22
range: [0, 22]
loc:
start:
line: 1
column: 0
end:
line: 4
column: 4
testAstLocationData '<>abc{}</>',
type: 'JSXFragment'
openingFragment:
start: 0
end: 2
range: [0, 2]
loc:
start:
line: 1
column: 0
end:
line: 1
column: 2
closingFragment:
start: 7
end: 10
range: [7, 10]
loc:
start:
line: 1
column: 7
end:
line: 1
column: 10
children: [
start: 2
end: 5
range: [2, 5]
loc:
start:
line: 1
column: 2
end:
line: 1
column: 5
,
start: 5
end: 7
range: [5, 7]
loc:
start:
line: 1
column: 5
end:
line: 1
column: 7
]
start: 0
end: 10
range: [0, 10]
loc:
start:
line: 1
column: 0
end:
line: 1
column: 10
test "AST as expected for Try node", ->
testAstLocationData 'try cappuccino',
type: 'TryStatement'