Skip to content

MVP: Support Sandpack snippets in TypeScript #5426

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

Draft
wants to merge 16 commits into
base: main
Choose a base branch
from

Conversation

eps1lon
Copy link
Member

@eps1lon eps1lon commented Dec 30, 2022

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 #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):

  • don't allow switching target language if only JS is available
  • 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).
  • 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
  • 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?))

@vercel
Copy link

vercel bot commented Dec 30, 2022

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)

aria-pressed={isCurrent ? true : undefined}
className={classNames(
'text-sm text-primary dark:text-primary-dark inline-flex mx-1 border-black',
isCurrent && 'border-b-4'
Copy link
Member Author

Choose a reason for hiding this comment

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

Tried aria-pressed:border-b-4 but this didn't work. Either I misunderstood that API or it would require a Tailwind bump which seems excessive.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yea this is available with 3.2.0 https://github.com/tailwindlabs/tailwindcss/releases/tag/v3.2.0 may be we can bump the package? its a minor bump only?


files['/styles.css'] = {
code: [sandboxStyle, files['/styles.css']?.code ?? ''].join('\n\n'),
hidden: !files['/styles.css']?.visible,
hidden: true,
Copy link
Member Author

Choose a reason for hiding this comment

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

Will extract this change which was revealed by less any typing.

Copy link
Member Author

Choose a reason for hiding this comment

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

// Assuming that if one file is available as TS, every file is.
// Assuming a JS version is available all the time.
if (snippetTargetLanguage === 'ts' && isJSFile(filePath) && hasTSVersion) {
delete fileMap[filePath];
Copy link
Member Author

Choose a reason for hiding this comment

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

Deleting instead of hidden: true since hidden files would still be part of the created CodeSandbox.

@github-actions
Copy link

github-actions bot commented Dec 30, 2022

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.

@harish-sethuraman
Copy link
Collaborator

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

@eps1lon
Copy link
Member Author

eps1lon commented Dec 30, 2022

#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.

@gaearon
Copy link
Member

gaearon commented Dec 31, 2022

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.

@eps1lon
Copy link
Member Author

eps1lon commented Dec 31, 2022

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
Copy link
Member Author

eps1lon commented Dec 31, 2022

@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 #5192.

@@ -24,6 +26,7 @@ async function markdownToHtml(markdown) {
.use(images)
.use(unrwapImages)
.use(smartyPants)
.use(sandpackTargetLanguages)
Copy link
Member Author

Choose a reason for hiding this comment

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

I don't understand why the plugins are listed in remarkPlugins and composed via .use. Just .use didn't produce any change.

@@ -55,7 +55,7 @@ function reviveNodeOnClient(key, val) {

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ~~~~ IMPORTANT: BUMP THIS IF YOU CHANGE ANY CODE BELOW ~~~
const DISK_CACHE_BREAKER = 7;
const DISK_CACHE_BREAKER = 8;
Copy link
Member Author

Choose a reason for hiding this comment

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

Bumped this for safe measure since I wasn't sure if "ANY CODE BELOW" also includes changes to imported code.

Hidden files would be part of the CodeSandbox ("Open in CodeSandbox" action.
- would crash if both TS and JS version are manually authored
- Should we throw if we failed to compile TS? Now we just warn and display the TS version
Makes computing JS filename from TS filename complicated
…ompile"

This reverts the malformed TS introduced in commit c1e6807.
Resolve the TODO since throwing seems preferrable
@gaearon
Copy link
Member

gaearon commented Jan 24, 2023

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.

@eps1lon
Copy link
Member Author

eps1lon commented Jan 24, 2023

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.

@gaearon
Copy link
Member

gaearon commented Jan 24, 2023

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.

@eps1lon
Copy link
Member Author

eps1lon commented Jan 24, 2023

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?

@gaearon
Copy link
Member

gaearon commented Feb 14, 2023

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.

@eps1lon eps1lon linked an issue Feb 16, 2023 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Question - Beta docs] Is there TypeScript version of examples?
4 participants