FormidableLabs / react-live

A flexible playground for live editing React components

Home Page:https://commerce.nearform.com/open-source/react-live/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Preceding semicolons (e.g. added by Prettier) cause `Invalid token` crash after code transforms

AlanSl opened this issue · comments

If a code block starts with ;, it will crash after React Live wraps it in () because while ; is a valid way to precede an expression prior to being wrapped, it's not a valid token to follow ( as the start of one expression.

Syntax Error: unexpected token (1:8)
1 : return (; ...
       ^

One reason a code block may start with ; is if Prettier is run on the code blocks (e.g. if it applies to JS within MDX), and Prettier's semicolon option is set to false, and a block starts with an arrow function or array.

Prettier will always insert a ; at the start of the line in this case, to guard against the array or arrow function's arguments being treated as an index or function call.

For example, Prettier with semicolons: false will transform this:

['a', 'b', 'c'].map(item => <SomeComponent someProp={item} />)

...to this, which is valid (if over-cautious) JSX:

;['a', 'b', 'c'].map(item => <SomeComponent someProp={item} />)

...but then React Live transforms it to this:

(;['a', 'b', 'c'].map(item => <SomeComponent someProp={item} />))

...and that crashes because (; is invalid.


Proposed fix

There's already a regex find-replace to remove trailing ;:

export const generateElement = ({ code = "", scope = {} }, errorCallback) => {
  // NOTE: Remove trailing semicolon to get an actual expression.
  const codeTrimmed = code.trim().replace(/;$/, "");

I think it could be fixed very easily by also removing preceding ;.

export const generateElement = ({ code = "", scope = {} }, errorCallback) => {
  // Removes preceding or trailing semicolon to get expression that can be wrapped in ()
  const codeTrimmed = code.trim().replace(/(^;|;$)/g, "");

Workaround

It's difficult to work around without either losing Prettier formatting JSX Live blocks, or setting Prettier's semicolon setting to true (forcing semicolons onto every line). In some cases it's possible to work around by wrapping the block in a non-arrow function:

function () { return ['a', 'b', 'c'].map(item => <SomeComponent someProp={item} />)) }