Preceding semicolons (e.g. added by Prettier) cause `Invalid token` crash after code transforms
AlanSl opened this issue · 0 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} />)) }