shikijs / twoslash

You take some Shiki, add a hint of TypeScript compiler, and 🎉 incredible static code samples
https://shikijs.github.io/twoslash/
MIT License
1.08k stars 53 forks source link

twoslash choking on second type parameter of generics #120

Open johnnyreilly opened 3 years ago

johnnyreilly commented 3 years ago

Heya,

I'm continuing my experiments using shiki-twoslash on my blog here: https://github.com/johnnyreilly/blog.johnnyreilly.com/pull/95

This morning I regexed my way to glory, converting all ts / js code samples to opt into twoslash. Think it came to about 100 files in total. Initially I thought there was just the (expected) out of memory issue captured in #117

But actually it looks like there's more. Running yarn start locally, when the blog actually cranks up I see an error like this:

image

Hopefully you can make out that there's a red caret underneath the second type parameter of generics. To grab the last one from the list it looks like this:

SyntaxError: /home/john/code/github/blog.johnnyreilly.com/blog-website/blog/2021-05-01-blog-archive-for-docusaurus.md: Unexpected token (231:26)
  229 |   const yearPosts = posts.get(year) || [];
  230 |   return posts.set(year, [post, ...yearPosts]);
> 231 | }, /** @type {Map`}<string, parentName="code" {...{"blogpost[]":""}}>{`}>} */ new Map());

It appears that some kind of transformation seems to have happened to the second type parameter which is tripping up twoslash. I couldn't say why this is happening - but it's happening.

The original code sample looks like this:

const postsByYear = allPosts.reduceRight((posts, post) => {
  const year = post.date.split('-')[0];
  const yearPosts = posts.get(year) || [];
  return posts.set(year, [post, ...yearPosts]);
}, /** @type {Map<string, BlogPost[]>}>} */ new Map());

It looks like BlogPost[] has become parentName="code" {...{"blogpost[]":""}} in this case. Not sure why

To try it out follow the instructions in this PR pertaining to running locally: https://github.com/johnnyreilly/blog.johnnyreilly.com/pull/95

johnnyreilly commented 3 years ago

It occurred to me that (as far as I know) the docusaurus-preset-shiki-twoslash doesn't remove Prism.JS from Docusaurus, and I pondered if that might be relevant. I suspect it's probably not as, to my knowledge, Prism.JS does it's work at runtime as compared to Shiki at build time. But I thought I'd mention it as an idea in case there's something I haven't considered

orta commented 3 years ago

Unsure, but there's no HTML generated by shiki/shiki-twoslash which includes something like parentName=" - and the code sample works fine in the playground, https://shikijs.github.io/twoslash/playground?#code/MYewdgzgLgBAhgGwQBRNCAuGBvGATOKAUy2gCcBLMAcxgF8BtAXRgF4ZmAoT0SWABzRQIAIQCeATSJwybeElToAdGSJ4ArsCIAlCtQAWUABRHB6ADQwzUAJRsAfDk4wYvaDDHTZ7a0oLElCH4ECmMAcgBaMJsGAAYmAG5nV3B3TxlFYTlrCCVqImN0sjsAHxKOROTVKHUyMCshXIgCoyLLBmtLJW6izIgmGyS6SwB6ACoxmAABKDF+IhwAWTh+AB5yKmpLEQQQakzmezojmDGRmDAiAHcYZf4jG0HOIA

It indeed must be something influenced by docusaurus

johnnyreilly commented 3 years ago

I had a root around in Prism.JS and didn't spot anything. Looked in Docusaurus too, and whilst I didn't spot anything in the code I did see this issue which referenced parentName in relation to markdown files (and all the code samples will be coming from markdown files)

https://github.com/facebook/docusaurus/issues/4489

Looks like the mdx transformation is invoked here: https://github.com/facebook/docusaurus/blob/main/packages/docusaurus-mdx-loader/src/index.ts

I suspect there's an issue with how https://github.com/mdx-js/mdx is being used. Not sure what though.

Given that type parameters sit inside < brackets which are what all HTML tags are made of, my guess would be that it's something to do with that

frencojobs commented 3 years ago

Given that type parameters sit inside < brackets which are what all HTML tags are made of, my guess would be that it's something to do with that

Maybe but it hasn't happened with TypeScript code afaik. For example, https://www.remotion.dev/docs/freeze also got quite a few JSX tags inside code samples and it works just fine. It's also built with Twoslash & Docusaurus.

johnnyreilly commented 3 years ago

Do you have any examples with generics with two or more type parameters?

frencojobs commented 3 years ago

Yep you're right. Generics with 2+ parameters are failing. But it's pretty random though. For example, this is working, image

orta commented 3 years ago

It's a push, but can you try it without the typo in the JSDoc comment?

const postsByYear = allPosts.reduceRight((posts, post) => {
  const year = post.date.split('-')[0];
  const yearPosts = posts.get(year) || [];
  return posts.set(year, [post, ...yearPosts]);
-}, /** @type {Map<string, BlogPost[]>}>} */ new Map());
+}, /** @type {Map<string, BlogPost[]>} */ new Map());
johnnyreilly commented 3 years ago

image

Done - no dice

SyntaxError: /home/john/code/github/blog.johnnyreilly.com/blog-website/blog/2021-05-01-blog-archive-for-docusaurus.md: Unexpected token (231:26)
  229 |   const yearPosts = posts.get(year) || [];
  230 |   return posts.set(year, [post, ...yearPosts]);
> 231 | }, /** @type {Map`}<string, parentName="code" {...{"blogpost[]":""}}>{`}>} */ new Map());
      |                           ^
  232 |
  233 | const yearsOfPosts = Array.from(postsByYear, ([year, posts]) => ({
  234 |   year,

from

const postsByYear = allPosts.reduceRight((posts, post) => {
  const year = post.date.split('-')[0];
  const yearPosts = posts.get(year) || [];
  return posts.set(year, [post, ...yearPosts]);
}, /** @type {Map<string, BlogPost[]>} */ new Map());