-
Notifications
You must be signed in to change notification settings - Fork 7.7k
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
eps1lon
wants to merge
16
commits into
reactjs:main
Choose a base branch
from
eps1lon:typescript-demos
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
03ba759
Improve type-safety when using `createFileMap`
eps1lon f0d6705
Update beta/src/components/MDX/Sandpack/SandpackRoot.tsx
eps1lon 13880e9
MVP: Support snippets in TypeScript
eps1lon a70e3f1
Switch snippet target language globally
eps1lon fe5a17e
Only hide JS if TS is available
eps1lon 4b36d9e
Demo with multi-file snippets
eps1lon 1493104
Remove files instead of hide them
eps1lon f08d6e1
Compile JS version from TS
eps1lon 5822741
Update beta/src/content/learn/keeping-components-pure.md
eps1lon c53a4fc
Drop support for mjs and mts
eps1lon 4c417a9
Don't allow switching target language if only JS is available
eps1lon 7338361
Ensure import graph actually uses TS
eps1lon c1e6807
REVERT BEFORE MERGE: Test what happens in prod if unable to compile
eps1lon 80d45e9
Revert "REVERT BEFORE MERGE: Test what happens in prod if unable to c…
eps1lon 318bdb9
Improve error reporting when failing to compile
eps1lon 32b5a2a
Ensure newly required Babel presets are installed
eps1lon File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
const {transform} = require('@babel/core'); | ||
const path = require('path'); | ||
const prettier = require('prettier'); | ||
const visit = require('unist-util-visit'); | ||
|
||
/** | ||
* Adds an additional JS codeblock to <Sandpack> children which which is based on the corresponding TypeScript codeblock. | ||
* The resulting JS code is formatted with Prettier. | ||
*/ | ||
module.exports = () => { | ||
return async function transformer(tree, file) { | ||
const prettierConfig = await prettier.resolveConfig( | ||
file.path ?? | ||
// TODO: When testing, `file.path` was always undefined | ||
// Now the formatting of the TS codeblock might use a different formatting config than the resolved config here. | ||
// E.g. when `content/learn/.prettierrc` exists it would be ignored at this point. | ||
file.cwd | ||
); | ||
|
||
visit(tree, 'mdxJsxFlowElement', (node) => { | ||
if (node.name === 'Sandpack') { | ||
const childrenWithJSTargetLanguage = node.children.flatMap((child) => { | ||
if ( | ||
child.type === 'code' && | ||
(child.lang === 'tsx' || child.lang === 'ts') | ||
) { | ||
const codeTSNode = child; | ||
// By default we assume `/App.tsx` | ||
// TODO: We should just require a filename i.e. `meta` so that we don't have an assumption that spreads throughout the codebase. | ||
const [tsFileName = '/App.tsx', ...rest] = | ||
codeTSNode.meta?.split(' ') ?? []; | ||
// Gallery.tsx -> Gallery.js | ||
// data.ts -> data.js | ||
const jsFileName = tsFileName.replace(/\.(ts|tsx)$/, '.js'); | ||
const meta = [jsFileName, ...rest].join(' '); | ||
const codeTS = codeTSNode.value; | ||
let codeJS = codeTS; | ||
try { | ||
codeJS = transform(codeTS, { | ||
filename: tsFileName, | ||
presets: [ | ||
[ | ||
'@babel/preset-typescript', | ||
{allExtensions: true, isTSX: tsFileName.endsWith('.tsx')}, | ||
], | ||
], | ||
}).code; | ||
codeJS = prettier.format(codeJS, { | ||
...prettierConfig, | ||
filepath: jsFileName, | ||
}); | ||
} catch (error) { | ||
throw new Error( | ||
`Failed to compile ${tsFileName}:\n${codeTS}\n${error}` | ||
); | ||
} | ||
const codeJSNode = { | ||
type: 'code', | ||
lang: 'js', | ||
meta, | ||
value: codeJS, | ||
}; | ||
|
||
// We can't just append since this would result in a different order of snippets if some snippets are not TS and visible: | ||
// App.tsx, styles.css -> styles.css, App.js | ||
// So we splice the transpiled version in instead: | ||
// App.tsx, styles.css -> App.tsx, App.js, styles.css | ||
return [codeTSNode, codeJSNode]; | ||
} else { | ||
return child; | ||
} | ||
}); | ||
|
||
if (childrenWithJSTargetLanguage.length !== node.children) { | ||
node.children = childrenWithJSTargetLanguage; | ||
} | ||
} | ||
}); | ||
}; | ||
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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.