Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CS2] CSX spread attributes: <div {props…} /> #4607

Merged
merged 19 commits into from
Aug 3, 2017
Merged
26 changes: 19 additions & 7 deletions lib/coffeescript/lexer.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 23 additions & 9 deletions lib/coffeescript/nodes.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 11 additions & 3 deletions src/lexer.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ exports.Lexer = class Lexer
@importSpecifierList = no # Used to identify when in an IMPORT {...} FROM? ...
@exportSpecifierList = no # Used to identify when in an EXPORT {...} FROM? ...
@csxDepth = 0 # Used to optimize CSX checks, how deep in CSX we are.
@csxSpreadProps = no # Used to detect if CSX attributes include spreads (<div {props...} />).

@chunkLine =
opts.line or 0 # The start line for the current @chunk.
Expand Down Expand Up @@ -488,6 +489,8 @@ exports.Lexer = class Lexer
# CSX is like JSX but for CoffeeScript.
csxToken: ->
firstChar = @chunk[0]
# Check the previous token to detect if attribute is spread.
prevChar = if @tokens.length > 0 then @tokens[@tokens.length - 1][0] else ''
if firstChar is '<'
match = CSX_IDENTIFIER.exec @chunk[1...]
return 0 unless match and (
Expand All @@ -512,8 +515,13 @@ exports.Lexer = class Lexer
@csxDepth--
return 2
else if firstChar is '{'
token = @token '(', '('
@ends.push {tag: '}', origin: token}
if prevChar == ':'
token = @token '(', '('
@ends.push {tag: '}', origin: token}
@csxSpreadProps = no
else
@ends.push {tag: '}'}
@csxSpreadProps = yes
return 1
else if firstChar is '>'
# Ignore terminators inside a tag.
Expand All @@ -540,7 +548,7 @@ exports.Lexer = class Lexer
else if @atCSXTag 1
if firstChar is '}'
@pair firstChar
@token ')', ')'
@token ')', ')' unless @csxSpreadProps
@token ',', ','
return 1
else
Expand Down
25 changes: 18 additions & 7 deletions src/nodes.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -1171,7 +1171,7 @@ exports.Obj = class Obj extends Base
node.error 'cannot have an implicit value in an implicit object'

# Object spread properties. https://github.com/tc39/proposal-object-rest-spread/blob/master/Spread.md
return @compileSpread o if @hasSplat()
return @compileSpread o if @hasSplat() and not @csx

idt = o.indent += TAB
lastNoncom = @lastNonComment @properties
Expand All @@ -1195,6 +1195,7 @@ exports.Obj = class Obj extends Base
answer = []
answer.push @makeCode if isCompact then '' else '\n'
for prop, i in props
propHasSplat = prop instanceof Splat
join = if i is props.length - 1
''
else if isCompact and @csx
Expand All @@ -1219,12 +1220,22 @@ exports.Obj = class Obj extends Base
key = key.properties[0].name
prop = new Assign key, prop, 'object'
if key is prop
if prop.shouldCache()
[key, value] = prop.base.cache o
key = new PropertyName key.value if key instanceof IdentifierLiteral
prop = new Assign key, value, 'object'
else if not prop.bareLiteral?(IdentifierLiteral)
prop = new Assign prop, prop, 'object'
unless propHasSplat
if prop.shouldCache()
[key, value] = prop.base.cache o
key = new PropertyName key.value if key instanceof IdentifierLiteral
prop = new Assign key, value, 'object'
else unless prop.bareLiteral?(IdentifierLiteral)
prop = new Assign prop, prop, 'object'
else
# Spread in CSX.
prop = if @csx then new Literal "{#{prop.compile(o)}}" else prop
# Check if CSX attribute is valid.
if @csx and prop instanceof Assign
prop.variable.error "Unexpected token" unless prop.variable.unwrap() instanceof PropertyName
propVal = prop.value.unwrap()
unless ((propVal instanceof Parens or propVal instanceof StringWithInterpolations) and propVal.body instanceof Block) or propVal instanceof StringLiteral or propVal instanceof Obj
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we reformat this line to something like:

unless propVal instanceof StringLiteral or propVal instanceof Obj or
    ((propVal instanceof Parens or propVal instanceof StringWithInterpolations) and propVal.body instanceof Block)
  prop.value.error "..."

It might also benefit from a comment stating what is allowed (e.g. what is (propVal instanceof Parens or propVal instanceof StringWithInterpolations) and propVal.body instanceof Block ensuring?).

prop.value.error "expected wrapped or quoted CSX attribute"
if indent then answer.push @makeCode indent
prop.csx = yes if @csx
answer.push @makeCode ' ' if @csx and i is 0
Expand Down