react.dev icon indicating copy to clipboard operation
react.dev copied to clipboard

MVP: Support Sandpack snippets in TypeScript

Open eps1lon opened this issue 2 years ago • 12 comments

Preview:

The selected "target language" (JS or TS) is selected globally and defaults to TS. It's not persisted at the moment but should be (cookie or localStorage?).

If no TS version is available, we disable the toggle and display the toggle as if JS is selected: Screenshot from 2023-01-01 10-19-29

~Right now we require duplicating codeblocks in TS and JS (Approach A). This was mainly motivated by having CoreMirror decorations that may not be easy to convert from TS decorations to JS decorations. Though it's unclear yet whether we use them and how we would use them for multiple target languages.~

~Alternatively (approach B), we would either have the codeblocks in TypeScript (and transpile them to JS automatically) or just JavaScript (i.e. not have a TS version (yet)).~

Right now we transpile+format the TS version when parsing MDX to produce the JS version. This is nice from a snippet author perspective (just need to write TS) but results in inconsistent formatting (e.g. bracket spacing). Either we accept that, or revisit https://github.com/reactjs/reactjs.org/pull/5192.

IIRC @gaearon was against formatting the snippets which makes both approaches a bit more tricky since A. We can't easily determine if TS and JS are out-of-sync (by checking the transpiled+formatted TS version against the JS version). B. Just having a TS version would go against formatting since now the JS version is auto-formatted.

TODO (this PR):

  • [x] don't allow switching target language if only JS is available
  • [x] how to keep in sync (depends on implementation approach A or B). Answer: Only allow either JS version or TS version. Produce JS from TS (via Babel+Prettier).
  • [x] throw or warn when compiling TS version fails? Throwing since it prevents a deploy and should therefore be caught before merge in most scenarios: https://vercel.com/fbopensource/beta-reactjs-org/CX5xir7i1toJubsXn29zFYWEEWfj
  • [x] Test: TypeScript does not allow file extension in import and would not transpile it. Does this break codesandbox compat?
  • [ ] Question: TypeScript does not allow file extension in import and would not transpile it. Is future ESM compat an issue?
  • [ ] Missing "Download" action
  • [ ] Type checking

TODO (not necessarily this PR):

  • [ ] persistence
  • [ ] track progress (to drive community effort (should start before or after release?))

eps1lon avatar Dec 30 '22 14:12 eps1lon

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated
beta-reactjs-org 🔄 Building (Inspect) Jan 1, 2023 at 9:40AM (UTC)
reactjs-org 🔄 Building (Inspect) Jan 1, 2023 at 9:40AM (UTC)

vercel[bot] avatar Dec 30 '22 14:12 vercel[bot]

Size Changes

📦 Next.js Bundle Analysis

This analysis was generated by the next.js bundle analysis action 🤖

⚠️ Global Bundle Size Increased

Page Size (compressed)
global 89.16 KB (🟡 +21 B)
Details

The global bundle is the javascript bundle that loads alongside every page. It is in its own category because its impact is much higher - an increase to its size means that every page on your website loads slower, and a decrease means every page loads faster.

Any third party scripts you have added directly to your app using the <script> tag are not accounted for in this analysis

If you want further insight into what is behind the changes, give @next/bundle-analyzer a try!

Three Pages Changed Size

The following pages changed size from the code in this PR compared to its base branch:

Page Size (compressed) First Load
/404 43.99 KB (🟡 +360 B) 133.15 KB
/500 43.97 KB (🟡 +360 B) 133.13 KB
/[[...markdownPath]] 44.06 KB (🟡 +360 B) 133.21 KB
Details

Only the gzipped size is provided here based on an expert tip.

First Load is the size of the global bundle plus the bundle for the individual page. If a user were to show up to your website and land on a given page, the first load size represents the amount of javascript that user would need to download. If next/link is used, subsequent page loads would only need to download that page's bundle (the number in the "Size" column), since the global bundle has already been downloaded.

Any third party scripts you have added directly to your app using the <script> tag are not accounted for in this analysis

Next to the size is how much the size has increased or decreased compared with the base branch of this PR. If this percentage has increased by 10% or more, there will be a red status indicator applied, indicating that special attention should be given to this.

github-actions[bot] avatar Dec 30 '22 14:12 github-actions[bot]

https://github.com/reactjs/reactjs.org/pull/4720 @eps1lon Are you implementing something similar to this?

harish-sethuraman avatar Dec 30 '22 15:12 harish-sethuraman

#4720 @eps1lon Are you implementing something similar to this?

I don't think so. My PR just about having the Sandpack snippets in TypeScript. From the PR description in #4720, it doesn't sound like we have any overlap.

Though #4720 will eventually benefit from this PR since the snippets will have more accurate completions for snippets written in TypeScript.

eps1lon avatar Dec 30 '22 18:12 eps1lon

It would be interesting to explore a version that compiles from just TS source. For line or character highlights we can use inline syntax like special comments so that they are automatically translated. I guess auto-formatting is the way to go in that case.

gaearon avatar Dec 31 '22 04:12 gaearon

For line or character highlights we can use inline syntax like special comments so that they are automatically translated.

Do you have an example where we currently use line or character highlights? Just so I can test different implementations.

eps1lon avatar Dec 31 '22 05:12 eps1lon

@gaearon Now compiles from just the TS version. Produces inconsistent formatting though:

Right now we transpile+format the TS version when parsing MDX to produce the JS version. This is nice from a snippet author perspective (just need to write TS) but results in inconsistent formatting (e.g. bracket spacing). Either we accept that, or revisit https://github.com/reactjs/reactjs.org/pull/5192.

eps1lon avatar Dec 31 '22 15:12 eps1lon

Are there any other sites that do this with good UX? I find it a bit confusing that toggling resets the edits although I get why. It's also confusing I don't see actual type errors if I make a mistake though I assume that's fixable. I do agree that we should probably just enable formatting everywhere and use a consistent Prettier setting.

gaearon avatar Jan 24 '23 13:01 gaearon

Are there any other sites that do this with good UX? I find it a bit confusing that toggling resets the edits although I get why.

Yeah I haven't thought about it. I think it's mostly an issue if you accidentally change the language. I think this is going to be quite the effort to save edits so that you can restore them when you change back the language.

It's also confusing I don't see actual type errors if I make a mistake though I assume that's fixable.

Hopefully just a setting for the Sandbox I need to enable. Will do in this PR. Do we lint the snippets?

Are there any other sites that do this with good UX?

MUI has it but I did this as well so not so sure about the "good UX" part 😁 Doesn't have editable when I last checked. Ant design has a switch as well but also not editable.

eps1lon avatar Jan 24 '23 13:01 eps1lon

Do we lint the snippets?

Yeah, we load ESLint with code splitting.

I think this is going to be quite the effort to save edits so that you can restore them when you change back the language.

Yeah I don't think we should save them, but I do think the way it works is a bit confusing. It's not clear if it "translates" from TS to JS or if it resets. I know it resets but I initially thought it would translate what I wrote lol.

gaearon avatar Jan 24 '23 14:01 gaearon

I know it resets but I initially thought it would translate what I wrote lol.

I think magically "upcasting" the JS+edits to TS+edits is out-of-scope (everybody switching to TS needs that 😉 ). For pristine snippets we don't have to do anything though.

When there are edits, we should probably ask if the user wants to keep the edits (potentially causing type-errors) or start fresh with the TypeScript code from the snippet. Not sure if that's easily doable (I remember something about prompts from iframes being deprecated?).

Though what do we do when the user resets a snippet that started as JS, had edits and then switched to TypeScripts? Reset to JS or reset to TS?

eps1lon avatar Jan 24 '23 14:01 eps1lon

I think we'd need a bit of designer help on this. We're busy with the landing but remind me to ask for help after.

gaearon avatar Feb 14 '23 14:02 gaearon