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

feat(config): Add .processCode config as a hook for complex usecases #262

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

jesstelford
Copy link
Contributor

@jesstelford jesstelford commented Sep 15, 2022

As added to the README:

Additional Code Transformations

A hook into the internal processing of code is available via the processCode option, which is a path to a file that exports a function that receives the code as entered into the editor, and returns the new code to be rendered.

One example is wrapping code in an IIFE for state support.


This change provides an escape hatch to fix #66, like so:

Create a new file processCode.ts in our repo:

export default function processCode(code: string) {
  // Wrapping in an IIFE gives us scope to execute arbitrary JS such as React.useState, etc
  // See: https://github.com/seek-oss/playroom/issues/66#issuecomment-714557367
  return `{(() => {${code}})()}`;
}

Add it to our playroom.config.js:

module.exports = {
  // ...
  processCode: './playroom/process-code.ts',
}

Fire up Playroom, and add the following code:

const [count, setCount] = React.useState(0);

return <button onClick={() => setCount(count + 1)}>Count: {count}</button>

Screen Shot 2022-09-15 at 12 24 04 pm


This PR was co-authored by @gwyneplaine

@jesstelford jesstelford marked this pull request as ready for review September 15, 2022 02:32
@jesstelford jesstelford requested a review from a team as a code owner September 15, 2022 02:32
@jesstelford jesstelford mentioned this pull request Sep 15, 2022
@jesstelford
Copy link
Contributor Author

jesstelford commented Sep 16, 2022

For a slightly more complex example of what processCode enables, we're now using this to handle the example @markdalgleish outlined in the OP of #66 :

export default function processCode(code: string) {
  // Everything after the last blank link is considered "rendered"
  const codeLines = code.trim().split('\n\n');
  const jsx = codeLines.pop() || '';

  // Wrap the entire code block in an IIFE so we have a scope where
  // arbitrary JS (including React state) can be executed
  return `{
      (() => {
        ${codeLines.join('\n\n')}
        ${
          // When there's already a `return`, leave it as-is
          jsx.trim()?.startsWith('return')
            ? jsx
            // Otherwise, wrap it in a return + fragment for safe rendering.
            : `return (
            <>
              ${jsx}
            </>
          )`
        }
      })()
  }`;
}

Some examples of the above code working:

Screen Shot 2022-09-16 at 5 15 59 pm

Screen Shot 2022-09-16 at 5 16 19 pm

Screen Shot 2022-09-16 at 5 18 12 pm

Screen Shot 2022-09-16 at 5 16 34 pm

@jesstelford
Copy link
Contributor Author

Some investigation reveals that code formatting does not consider any changes returned from processCode. In the OP example, the code is formatted oddly, because prettier doesn't understand that it's wrapped in the IIFE.

If we were to call processCode before the formatter, we'd end up inserting the IIFE into the Playroom editor, which is not what we want.

For our particular usecase, we've disabled formatting by patching our installation of Playroom, but that's not a great solution.

I'm not sure if this issue should block the PR or not?

@alex-page
Copy link

@seek-oss/playroom-maintainers any interest in this contribution?

@markdalgleish
Copy link
Member

We're definitely keen on exploring this! I do think the code formatting issue is a show-stopper, so I don't think we should open up such a low level feature if it won't integrate cleanly with the rest of Playroom. It might be worth exploring more high-level, built-in support for multiple expressions beyond just JSX.

I can see a couple of viable options for this:

  • We could use syntax similar to what you showed where you can write multiple expressions with an implicit return of JSX at the end.

    I can see this requiring some AST transformations to handle the implicit return — splitting on multiple line breaks won't work because this is valid within your JSX.

    Managing the formatting of it could be tricky too since it's not valid code as it's written. We already have to wrap everything in a fragment behind the scenes to make formatting of sibling JSX elements work, and then remove the fragment from the formatted code afterwards. This would need to be an even more complicated version of that.

    I do have some concerns with this approach since it's no longer "just JSX" and instead becomes a bit of a weird hybrid. It might feel a bit unclear what the syntax rules are. Maybe it's ok.

  • We could keep the JSX handling as it is now, but add an additional opt-in pane to the UI that contains the setup code where you can put your hooks etc. The challenge here will be maintaining Playroom's simplicity, which to me is a big part of what sets it apart.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support state
3 participants