Twoslash In Web Generator Docs: A Client-Side Rendering Approach
Hey guys! Let's dive into a cool idea about enhancing our web generator documentation. The goal? To make it even more interactive and helpful using twoslash. I'm thinking we can make this a completely optional, client-side feature so it doesn't bloat our initial load times or bundle sizes. I've got a few ideas on how to pull this off, and I'm really looking for your thoughts and suggestions.
The Idea: Generic CSR Snippet
The core concept here is to implement a generic Client-Side Rendering (CSR) snippet. This snippet would be responsible for traversing all the existing <pre> elements in our documentation. Once it finds these code blocks, it grabs the raw code inside. Then, it re-runs Shiki (our syntax highlighter) with Twoslash enabled. This process effectively adds those sweet, interactive TypeScript tooltips and annotations directly into our code examples.
Why This Approach?
Implementing Twoslash this way offers several advantages:
- It's Optional: By making it a CSR feature, users who don't need or want Twoslash won't have to download the extra baggage. It's a progressive enhancement!
- No Impact on Initial Load: The heavy lifting of running Shiki and Twoslash happens after the initial page load. This keeps our HTML sizes and bundle sizes lean and mean.
- Desktop-Friendly: We can limit this feature to desktop environments, where users are more likely to benefit from the detailed code insights that Twoslash provides. This avoids unnecessary processing on mobile devices.
- Technology Agnostic: It doesn't necessarily rely on React. The basic implementation would target
<pre>elements directly, making it more versatile across different parts of our documentation, even if they're not React-based.
Addressing the React "Walled Garden"
Now, here's where things get a bit interesting. The approach I've described technically bends the rules of React's component-based architecture. React typically prefers that components manage their own rendering and updates within their own little world. Directly manipulating the DOM outside of React components can sometimes lead to unexpected behavior or conflicts.
Option 1: Embrace the Snippet
We could go with the straight-up CSR snippet, acknowledging that it's a bit of a hack but potentially the simplest and most direct solution. This keeps the implementation lean and makes it easier to maintain in the short term. The downsides are that you must be very careful with any conflicting events.
Option 2: The React Hook
To play nicer with React, we could create a custom React Hook. This Hook would be responsible for re-hydrating (re-running Shiki with Twoslash) on the client-side. The Hook would essentially do the same thing as the CSR snippet – find <pre> elements, grab the code, and run Shiki with Twoslash – but it would do it within the context of a React component.
Benefits of the React Hook Approach:
- React-Friendly: Keeps our code within React's ecosystem, potentially avoiding conflicts and making it easier to reason about.
- Component-Specific: Allows us to apply Twoslash to specific components or sections of the documentation, giving us more fine-grained control.
Drawbacks of the React Hook Approach:
- More Complex: Adds a layer of complexity compared to the simple CSR snippet.
- React Dependency: Ties the Twoslash implementation to React, making it harder to use in non-React parts of the documentation.
Example Implementation (Conceptual)
Here's some pseudocode to illustrate the basic idea behind the CSR snippet:
// This code is illustrative and not production-ready
function applyTwoslash() {
const preElements = document.querySelectorAll('pre');
preElements.forEach(async (pre) => {
const code = pre.textContent;
// Run Shiki with Twoslash (assuming you have a function for this)
const highlightedCode = await runShikiWithTwoslash(code);
// Replace the original code with the highlighted code
pre.innerHTML = highlightedCode;
});
}
// Run the function when the DOM is ready
document.addEventListener('DOMContentLoaded', applyTwoslash);
And here's a rough idea of how the React Hook might look:
import { useEffect } from 'react';
function useTwoslash(ref) {
useEffect(() => {
async function applyTwoslash() {
if (!ref.current) return;
const code = ref.current.textContent;
const highlightedCode = await runShikiWithTwoslash(code);
ref.current.innerHTML = highlightedCode;
}
applyTwoslash();
}, [ref]);
}
export default useTwoslash;
// Usage:
function MyComponent() {
const codeRef = useRef(null);
useTwoslash(codeRef);
return (
<pre ref={codeRef}>
{/* Your code here */}
</pre>
);
}
Considerations and Questions
Before we jump into implementation, here are some things we should think about:
- Performance: How will running Shiki with Twoslash on the client affect performance, especially on lower-end devices? We might need to implement some throttling or debouncing to avoid janky animations.
- Caching: Can we cache the results of running Shiki with Twoslash to avoid re-processing the same code blocks multiple times?
- Error Handling: What happens if Shiki or Twoslash fails to run? We need to gracefully handle errors and provide informative feedback to the user.
- Configuration: Should we allow users to configure Twoslash options (e.g., disabling certain features)?
- Integration with Existing Tooling: How will this integrate with our existing documentation tooling and build process?
Open to Suggestions!
I'm really keen to hear your thoughts on this. Which approach do you think is best? Do you have any other ideas on how to implement Twoslash in our documentation? Let's brainstorm and come up with the best solution for our users!
What do you think, guys? Let's make our documentation awesome!