Skip to content

Automatically create JS versions of our TS code in the docs #2638

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

Merged
merged 29 commits into from
May 13, 2025

Conversation

cprecioso
Copy link
Member

@cprecioso cprecioso commented Apr 8, 2025

Description

Part of #2455

This PR allows to just write TS code directly, and automatically transform it and appear with the JS/TS selector.

It does so by adding two plugins to Docusaurus' MDX handling:

  • autoJSCode will find any code block with the auto-js meta, transform it with ts-blank-space, and remove the extra blank space with prettier.

    Example transformation

    Input:

     ```ts auto-js title="hello.ts"
     const foo = (n: number) => 8 as string
     ```

    Equivalent output (actual output is done in AST):

     <Tabs groupId="js-ts">
     <TabItem value="js" label="JavaScript">
     
     ```js title="hello.js"
     const foo = (n) => 8
     ```
     
     </TabItem>
     <TabItem value="ts" label="TypeScript">
     
     ```ts title="hello.ts"
     const foo = (n: number) => 8 as string
     ```
     
     </TabItem>
     </Tabs>
    Why use `ts-blank-space`?

    Basically, it's the transformer that does the fewest modifications to the input code, which I think is important to keep the overall visual structure of the code in the examples. It also helps to not introduce TS-only constructs like enums that, when automatically converted, introduce distracting noise.

    I also feel validated in its approach since it uses the TypeScript compiler directly to do its job, and not a custom parser that might get outdated. As well, the blank-space-replacing method is used by SWC and Node.js in their TypeScript support.

  • codeWithHole is made to deal with some of our examples, that just omit some syntactically needed parts of the code with .... In order for the TypeScript transformation to work correctly, this plugin allows us to use an identifier named hole that will be replaced by ..., while still being syntactically correct.

    Example transformation

    Input:

     ```ts with-hole
     const foo = (n: number) => {
     	return {
     		name: "bar",
     		hole
     	}
     }
     ```

    Equivalent output (actual output is done in AST):

     ```ts
     const foo = (n: number) => {
     	return {
     		name: "bar",
     		/* ... */
     	}
     }
     ```

With these two plugins, we can transform our examples to be less complex to author while keeping the option to read our docs in JS or TS. If there are any examples that can't be automatically converted, or that shouldn't be converted, we still retain the option to do it manually as we've been doing until now.

I converted two examples so you can see the output, please check them out.

@cprecioso cprecioso requested review from Martinsos and infomiho April 8, 2025 09:32
@cprecioso cprecioso changed the title Automatically create JS versions of our TS code Automatically create JS versions of our TS code in the docs Apr 8, 2025
Copy link
Member

@Martinsos Martinsos left a comment

Choose a reason for hiding this comment

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

Very cool! Seems like an awesome thing to me, much less duplication! And we can still do it manually if we really need to (we can right? Would we then use those Tabs again?).

I will let others check the details of the PR and approve it, to make sure it works fully with how we do things currently (e.g. if you toggle to JS, does it remmeber the toggle? Mostly checking if we lost anything with not using our Tab system anymore). Aha but I see now that we actually do still use Tabs, they are produced by the plugin -> ok that sounds good then!

One th ing that would certainly be valuable is more documentation on this -> we should mention in docs README.md that we are using this mechanism because it is a bit magic, so having a central place with some discoverability where we describe it would be valuable.

Also, these remark/ files, we should provide some top level header comments in them, let's say one at the top of each file, to explain a bit the motivation and what are they here for, as they are a bit unexpected.
Also I didn't review the code in them, I will let the others do that.

Awesome all together!

@cprecioso
Copy link
Member Author

we should mention in docs README.md

top level header comments

Yes! I wanted to get this out to get some comments on the approach but I wanted to do this, I'll get on it

@Martinsos
Copy link
Member

we should mention in docs README.md

top level header comments

Yes! I wanted to get this out to get some comments on the approach but I wanted to do this, I'll get on it

No worries, I assumed so!
Advice: If you want the people to take a look at the PR, but not do a deep review -> make a draft PR. That basically communicates exactly that, that you are looking for comments, but you not done yet. While real PR means it is ready for proper reviewing.

@cprecioso cprecioso self-assigned this Apr 8, 2025
@cprecioso
Copy link
Member Author

Can I get a review? 🙏🏼

Copy link
Contributor

@infomiho infomiho left a comment

Choose a reason for hiding this comment

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

I love this idea. It will make our lives much easier.

Testing

I tried with some more complex examples:

<Tabs groupId="js-ts">

  • here the tricky part are the TS specific comments (which stay in the JS version), we'd probably need to rewrite those code blocks not to use those comments and move that info somewhere outside of the code block

Another example:

<Tabs groupId="js-ts">

We show Prisma code with the language switch for completeness sake - it toggles between different variants of the text below. So I guess, here we'll just use the tabs directly, you addressed it in the README, that's great 👍

In this example:

<Tabs groupId="js-ts">

The JS version still has the import statement (without anything imported):
Screenshot 2025-04-11 at 13 57 10

For me, the next step would be - try to convert all the code blocks to see where the current approach fails and try to address those cases. For some of the cases we'll need to modify the code (e.g. to exclude Typescript specific comments), that's okay for me.

@cprecioso
Copy link
Member Author

@infomiho

try to convert all the code blocks

Are we sure converting 230 occurrences of this pattern is a good use of the team's time? 😅
image

I was planning on us leaving stuff as it is; writing new stuff with this plugin, and converting on the go whenever we touch a file.

@infomiho
Copy link
Contributor

I think it's a valid use of team time since it will speed us the docs authoring process. It took me a couple of minutes per code block to do it, so it might be a few hours max IMHO.

What I looking to achieve: find the the edge cases for which the plugin might not produce 1:1 JS code. While this is still hot, while you have the context, I think it'll be good to figure out and write down the process/limits of the plugin.

Let's do it like this: invest one hour to convert as many examples as possible and try going for as many unique examples as possible 🙂

@cprecioso cprecioso marked this pull request as draft April 16, 2025 15:01
@cprecioso
Copy link
Member Author

Turning to draft until #2658 is merged

@cprecioso cprecioso changed the title Automatically create JS versions of our TS code in the docs WIP: Automatically create JS versions of our TS code in the docs Apr 25, 2025
@infomiho
Copy link
Contributor

infomiho commented May 5, 2025

I suppose this PR can go forward since #2658 is merged 🎉

@cprecioso
Copy link
Member Author

@infomiho ready for review

I tried updating the tutorial and some examples (https://github.com/wasp-lang/wasp/pull/2728/files), and the main drawbacks that I found were

  • We need to remember to add import type so they don't appear in JS
  • Edge cases:
    • // highlight-next-line comment before a TS-only line will hang around and highlight the wrong line (happened twice)
    • We're running prettier so in some cases where we're putting extra spaces around some lines to make them clearer and they get collapsed (only happened once)
    • It doesn't replace file names' extensions in clarification comments (I'd say this problem is almost unique to the tutorial)

All in all, those are edge cases that might be out of scope for this, and they're not too impactful. We can always write the tabs manually for whatever edge case arises, so this is a "strictly better" improvement IMO. I'm comfortable merging.

@cprecioso cprecioso marked this pull request as ready for review May 7, 2025 10:41
@cprecioso cprecioso changed the title WIP: Automatically create JS versions of our TS code in the docs Automatically create JS versions of our TS code in the docs May 7, 2025
Copy link
Contributor

@infomiho infomiho left a comment

Choose a reason for hiding this comment

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

Great stuff @cprecioso, I think we are almost there. I'm glad you tested it out on more code blocks to get a better sense of caveats. I think the benefits definitely outweigh the caveats.

I've left some more comments, let me know when I can take another look.

Comment on lines +131 to +147
```tsx title="src/pages/SomePage.tsx" auto-js with-hole
import React, { useEffect } from "react";
import { api } from "wasp/client/api";

async function fetchCustomRoute() {
const res = await api.get("/foo/bar");
console.log(res.data);
}

export const Foo = () => {
useEffect(() => {
fetchCustomRoute();
}, []);

return <>{hole}</>;
};
```
Copy link
Contributor

Choose a reason for hiding this comment

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

One thing I noticed that when I try to auto format this code in VS Code, the code block doesn't get formatted and the code in the output is of course formatted.

Screenshot 2025-05-12 at 13 17 44

Is there a way for us to configure Prettier to format the code blocks in the MDX files? I think it makes sense for code blocks in the source code to look as similar as possible to the rendered code blocks for easier searching later on.

If it's not possible to run Prettier on MDX files (this issue suggests so) should we make it a part of the docs authoring process to format the code before including it in the code blocks?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep, as you found out, Prettier is stuck in MDXv1 so I'd rather not run it on our MDXv3 files, it will mess up its delicate whitespace situation 😑

should we make it a part of the docs authoring process to format the code before including it in the code blocks?

You mean just adding that to the readme? I can do that.

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'd rather not have to run Prettier at all if we don't have it at the source, but it needs to run on the JS so it removes the blank space (I didn't find another type stripper that doesn't leave the whitespace), and then on the TS for consistency. 😔

Copy link
Contributor

@infomiho infomiho May 13, 2025

Choose a reason for hiding this comment

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

Yep, I thought maybe about adding a note in the README to say, hey, format it so the source code and the rendered code look as close as possible for better searchablity.

Too bad about missing MDX 3 support, since Docusaurus moved, you'd expect it to be more demand for it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe one for the future, npx remark . --ext .mdx --output should probably format MDX 3 if used with the remark-mdx plugin. You probably know more about this than me, just throwing it out there.

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 added it here BTW

- Run `prettier` on the code before pasting it in the document, as `auto-js` will enforce it.

cprecioso and others added 8 commits May 12, 2025 13:35
- Use `unist-util-visit`, no need for `unist-util-visit-parents`.
- Extract the common functions of the visitor check and the language assert to a shared file
- For the async visitor, create a thunk inside of the visitor so we keep all the type narrowing instead of having to do it again
Co-authored-by: Mihovil Ilakovac <mihovil@ilakovac.com>
Co-authored-by: Mihovil Ilakovac <mihovil@ilakovac.com>
@cprecioso
Copy link
Member Author

@infomiho ready

@cprecioso
Copy link
Member Author

@infomiho if everything is ready let's merge ahead of the sprint review? 😁

Copy link
Contributor

@infomiho infomiho left a comment

Choose a reason for hiding this comment

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

Naming comments, almost there

Copy link
Contributor

@infomiho infomiho left a comment

Choose a reason for hiding this comment

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

Great addition

@cprecioso cprecioso merged commit 5420929 into main May 13, 2025
@cprecioso cprecioso deleted the auto-js-docs branch May 13, 2025 12:07
Copy link
Member

@Martinsos Martinsos left a comment

Choose a reason for hiding this comment

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

I am writing remark plugin myself (coderef checker) and therefore got interested in this PR, so I started reviewing this PR while it was still unmerged, but it got merged in the meantime! Which is ok, I joined the party really late.

I however finished the review, so here it is. We can do initial discussion of items here, while you do the fixes in the new PR, and then we switch to that one after the first iteration of changes.

@cprecioso cprecioso restored the auto-js-docs branch May 15, 2025 13:35
@cprecioso cprecioso deleted the auto-js-docs branch May 15, 2025 13:37
@cprecioso
Copy link
Member Author

@Martinsos's review addressed in #2788

@Martinsos
Copy link
Member

Ok we took care of everything that was being discussed in this merged PR, the only conversation left is in the new PR (#2788 ), continuing only there.

cprecioso added a commit that referenced this pull request Jun 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants