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
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
3e59636
Automatically create JS versions of our TS code
cprecioso Apr 8, 2025
e0ee478
Apply two examples
cprecioso Apr 8, 2025
793b50f
Add file comments
cprecioso Apr 8, 2025
4002350
Refactor some code to make it more understandable
cprecioso Apr 8, 2025
3bd0591
Add readme docs
cprecioso Apr 8, 2025
fd19f6a
Comments in readme
cprecioso Apr 14, 2025
b255f6b
Fix admonition
cprecioso Apr 14, 2025
56112f3
Typo
cprecioso Apr 14, 2025
b954e89
More fixes
cprecioso Apr 14, 2025
49d7321
Simple changes
cprecioso Apr 14, 2025
1f68723
Find the correct prettier confirm
cprecioso Apr 14, 2025
f41ef1e
Extract functions
cprecioso Apr 14, 2025
d24c7ed
Merge remote-tracking branch 'origin/main' into auto-js-docs
cprecioso May 7, 2025
027943d
Forgot hoel
cprecioso May 7, 2025
3f3dfe3
Fix component
cprecioso May 7, 2025
0fafea8
Update plugins to ts
cprecioso May 7, 2025
2bc974e
Update to MDX v3
cprecioso May 7, 2025
c9d54e2
Correctly parse JSX
cprecioso May 7, 2025
2abdc9b
Add vfile reporting
cprecioso May 12, 2025
429bcdb
Refactor
cprecioso May 12, 2025
d968565
Apply suggestion
cprecioso May 12, 2025
aeec5a8
Apply suggestion
cprecioso May 12, 2025
36eb43a
Apply comment
cprecioso May 12, 2025
f7f3d69
Apply suggestion
cprecioso May 12, 2025
dbed0a0
Extract prettier config
cprecioso May 12, 2025
beed6b9
Add caveats to readme
cprecioso May 12, 2025
0154adc
Trim final newline
cprecioso May 12, 2025
7d4bd83
Rename fn
cprecioso May 13, 2025
3d6253d
Better explain the transforms
cprecioso May 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 80 additions & 2 deletions web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,84 @@ $ npm start
This command starts a local development server and opens up a browser window.
Most changes are reflected live without having to restart the server.

### Writing docs
Copy link
Member

Choose a reason for hiding this comment

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

There are quite a few headers already in this README about dealing with docs. They are also level 3 headers. It somehow feels like this specific header is the entry header for docs though. Would it make more sense to make this one level 2 header, call it ## Docs, and then put this at the start and the rest of the headers under it (which is already happening actually)?

Further question is if some of this content would be better suited in writingguide.md or even docs/README.md (which doesn't exist yet), but we don't have to deal with that now (but maybe in the future we will want to move most of this content into docs/README.md).

Copy link
Member Author

@cprecioso cprecioso May 15, 2025

Choose a reason for hiding this comment

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

We have web/readme.md#writing docs, web/writing-docs.md and web/docs/writingguide.md. It seems like there's an opportunity for centralization here but tbh I don't know where to start from, the end result would be a monster doc


To write docs, you can use Markdown or MDX. The docs are located in the `docs` directory.
Remember to refer to the [Writing Guide](https://wasp.sh/docs/writingguide) for an explanation
of how we like to write docs. You can check
[Docusaurus' documentation](https://docusaurus.io/docs/2.x/markdown-features) to see which special
Markdown features available (e.g. line highlighting).
Comment on lines +28 to +32
Copy link
Member

Choose a reason for hiding this comment

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

You will notice that in the rest of the markdown we (me for sure, I think others also) don't stick to 80/100 lines as the max col, but take as much space as we need. That is because editors do good job of visually wrapping long lines and it makes writing text easier (you don't need to keep formatting it as you edit it).
Instead, what I do is usually start each sentences in a new line. That makes it easy to move from sentence to sentence also. I am not sure we ever communicated this at the team level though, but it might be happening organically, or maybe it is just me and I think everybody else is also doing it hah :D. But I think it has its benefits?

Suggested change
To write docs, you can use Markdown or MDX. The docs are located in the `docs` directory.
Remember to refer to the [Writing Guide](https://wasp.sh/docs/writingguide) for an explanation
of how we like to write docs. You can check
[Docusaurus' documentation](https://docusaurus.io/docs/2.x/markdown-features) to see which special
Markdown features available (e.g. line highlighting).
To write docs, you can use Markdown or MDX. The docs are located in the `docs` directory.
Remember to refer to the [Writing Guide](https://wasp.sh/docs/writingguide) for an explanation
of how we like to write docs.
You can check [Docusaurus' documentation](https://docusaurus.io/docs/2.x/markdown-features) to see which special Markdown features available (e.g. line highlighting).



#### Polyglot code blocks

For examples that have a JavaScript and TypeScript version, add a `auto-js` meta attribute
to the code block, like so:
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
to the code block, like so:
to the TypeScript code block, like so:


~~~mdx
```ts title="src/apis.ts" auto-js
export const validatePassword = (password: string) => password.length > 8;
```
~~~

And it will automatically generate a JS and TS version with a selector to switch between them:
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
And it will automatically generate a JS and TS version with a selector to switch between them:
And it will automatically generate a JS version based on the TS one + a selector to switch between them:

otherwise it sound slike it generates both js and ts version, which is not what we wanted to say, right?


~~~mdx
<Tabs groupId="js-ts">
<TabItem value="js" label="JavaScript">

```js title="src/apis.js"
export const validatePassword = (password) => password.length > 8;
```

</TabItem>
<TabItem value="ts" label="TypeScript">

```ts title="src/apis.ts"
export const validatePassword = (password: string) => password.length > 8;
```

</TabItem>
</Tabs>
~~~

> [!NOTE]
> You can create a language switcher manually as described in
> [Docusaurus docs](https://docusaurus.io/docs/2.x/markdown-features/code-blocks#multi-language-support-code-blocks).

If you need to omit some part of the code in a code example, you can use the `with-hole` meta attribute
Copy link
Member

Choose a reason for hiding this comment

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

This is under "polyglot code blocks", but it has nothing to do with polyglot code blocks, right? It would make more sense to put explanation about "hole" under separate header. Or maybe you can rename the header to "code blocks". Even then, each of these parts, auto-js and with-hole, might benefit from their own headers.

which will add an ellipsis wherever you write the identifier `hole` in the code block, so you can keep
it syntactically valid. You can combine it with the `auto-js` tag.

For example, the following input:

~~~mdx
```ts title="src/apis.ts" auto-js with-hole
export const validatePassword = (password: string) => password.length > 8 && hole;
```
~~~

Will be transformed to:

~~~mdx
<Tabs groupId="js-ts">
<TabItem value="js" label="JavaScript">

```js title="src/apis.js"
export const validatePassword = (password) => password.length > 8 && ...;
```

</TabItem>
<TabItem value="ts" label="TypeScript">

```ts title="src/apis.ts"
export const validatePassword = (password: string) => password.length > 8 && ...;
```

</TabItem>
</Tabs>
~~~

### Build

```
Expand Down Expand Up @@ -56,14 +134,14 @@ We deploy the website to Cloudflare Pages. When you want to deploy changes from
git checkout release
```

The website should be live within a few minutes at https://wasp.sh.
The website should be live within a few minutes at https://wasp.sh.

You can track the deployment progress on Cloudflare Pages (https://dash.cloudflare.com/). Credentials are in the 1Password vault.

### Preview docs from the `main` branch

We set up automatic deployment of docs from the `main` branch on Cloudflare Pages. This means that every time you push to the `main` branch, the docs will be built and deployed to https://wasp-docs-on-main.pages.dev.

### Multiple documentation versions

We maintain docs for multiple versions of Wasp.
Expand Down
58 changes: 17 additions & 41 deletions web/docs/advanced/apis.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,47 +128,23 @@ For example, if your app is running at `https://example.com` then from the above

To use the API from your client, including with auth support, you can import the Axios wrapper from `wasp/client/api` and invoke a call. For example:

<Tabs groupId="js-ts">
<TabItem value="js" label="JavaScript">
```jsx title="src/pages/SomePage.jsx"
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 <>// ...</>;
};
```
</TabItem>

<TabItem value="ts" label="TypeScript">
```tsx title="src/pages/SomePage.tsx"
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 <>// ...</>;
};
```
</TabItem>
</Tabs>
```tsx title="src/pages/SomePage.tsx" auto-js with-hole
Copy link
Member

Choose a reason for hiding this comment

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

Nice, awesome!

I am curious, in which situations do we need this with-hole plugin, vs just ourselves putting /* ... */ in the code?

Hm I will also have to take this into account in the coderef checker plugin -> I will probably want to run it after the with-hole, so I can work with /* ... */, and not worry about the hole thing, since /* ... */ might be easier to detect. Not sure yet.

Copy link
Member Author

Choose a reason for hiding this comment

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

Indeed in this specific example the plugin is not that useful, as you can manually write the comment.

But it is useful in situations such as the one explained in the Readme:

const foo = myBool && /* ... */; // not syntactically valid
const bar = myBool && hole; // syntactically valid

We need examples to be syntactically valid as ts-blank-space won't work otherwise.

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}</>;
};
```
Comment on lines +131 to +147
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.


#### Making Sure CORS Works

Expand Down
131 changes: 41 additions & 90 deletions web/docs/auth/social-auth/google.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
title: Google
---

import { ShowForTs } from '@site/src/components/TsJsHelpers'
import useBaseUrl from '@docusaurus/useBaseUrl';
import DefaultBehaviour from './\_default-behaviour.md';
import OverrideIntro from './\_override-intro.md';
Expand Down Expand Up @@ -379,103 +380,53 @@ The fields you receive depend on the scopes you request. The default scope is se

<OverrideExampleIntro />

<Tabs groupId="js-ts">
<TabItem value="js" label="JavaScript">
```wasp title="main.wasp"
app myApp {
wasp: {
version: "{latestWaspVersion}"
},
title: "My App",
auth: {
userEntity: User,
methods: {
google: {
// highlight-next-line
configFn: import { getConfig } from "@src/auth/google.js",
// highlight-next-line
userSignupFields: import { userSignupFields } from "@src/auth/google.js"
}
},
onAuthFailedRedirectTo: "/login"
},
}
```

```prisma title="schema.prisma"
model User {
id Int @id @default(autoincrement())
username String @unique
displayName String
}

// ...
```

```js title=src/auth/google.js
export const userSignupFields = {
username: () => 'hardcoded-username',
displayName: (data) => data.profile.name,
}

export function getConfig() {
return {
scopes: ['profile', 'email'],
```wasp title="main.wasp"
app myApp {
wasp: {
version: "{latestWaspVersion}"
},
title: "My App",
auth: {
userEntity: User,
methods: {
google: {
// highlight-next-line
configFn: import { getConfig } from "@src/auth/google.js",
// highlight-next-line
userSignupFields: import { userSignupFields } from "@src/auth/google.js"
}
}
```
</TabItem>

<TabItem value="ts" label="TypeScript">
```wasp title="main.wasp"
app myApp {
wasp: {
version: "{latestWaspVersion}"
},
title: "My App",
auth: {
userEntity: User,
methods: {
google: {
// highlight-next-line
configFn: import { getConfig } from "@src/auth/google.js",
// highlight-next-line
userSignupFields: import { userSignupFields } from "@src/auth/google.js"
}
},
onAuthFailedRedirectTo: "/login"
},
}
```
},
onAuthFailedRedirectTo: "/login"
},
}
```

```prisma title="schema.prisma"
model User {
id Int @id @default(autoincrement())
username String @unique
displayName String
}
```prisma title="schema.prisma"
model User {
id Int @id @default(autoincrement())
username String @unique
displayName String
}

// ...
```
// ...
```

```ts title=src/auth/google.ts
import { defineUserSignupFields } from 'wasp/server/auth'
```ts title=src/auth/google.ts auto-js
import { defineUserSignupFields } from 'wasp/server/auth'

export const userSignupFields = defineUserSignupFields({
username: () => 'hardcoded-username',
displayName: (data: any) => data.profile.name,
})
export const userSignupFields = defineUserSignupFields({
username: () => 'hardcoded-username',
displayName: (data: any) => data.profile.name,
})

export function getConfig() {
return {
scopes: ['profile', 'email'],
}
}
```
export function getConfig() {
return {
scopes: ['profile', 'email'],
}
}
```

<GetUserFieldsType />
</TabItem>
</Tabs>
<ShowForTs><GetUserFieldsType /></ShowForTs>

## Using Auth

Expand Down
10 changes: 9 additions & 1 deletion web/docusaurus.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import type * as Preset from '@docusaurus/preset-classic'
import type { Config, DocusaurusConfig } from '@docusaurus/types'
import { themes } from 'prism-react-renderer'
import autoImportTabs from './src/remark/auto-import-tabs'
import autoJSCode from './src/remark/auto-js-code'
import codeWithHole from './src/remark/code-with-hole'
import fileExtSwitcher from './src/remark/file-ext-switcher'
import searchAndReplace from './src/remark/search-and-replace'

Expand Down Expand Up @@ -157,7 +159,13 @@ const config: Config = {
sidebarPath: './sidebars.ts',
sidebarCollapsible: true,
editUrl: 'https://github.com/wasp-lang/wasp/edit/release/web',
remarkPlugins: [autoImportTabs, fileExtSwitcher, searchAndReplace],
remarkPlugins: [
autoJSCode,
autoImportTabs,
fileExtSwitcher,
searchAndReplace,
codeWithHole,
],

// ------ Configuration for multiple docs versions ------ //

Expand Down
18 changes: 17 additions & 1 deletion web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,16 @@
"react-player": "^2.16.0",
"react-tooltip": "^4.5.1",
"react-transition-group": "^4.4.5",
"tailwindcss": "^3.2.4"
"tailwindcss": "^3.2.4",
"ts-blank-space": "^0.6.1",
"unist-util-visit-parents": "^6.0.1"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "^3.7.0",
"@docusaurus/tsconfig": "^3.7.0",
"@types/mdast": "^4.0.4",
"mdast": "^2.3.2",
"mdast-util-mdx": "^3.0.0",
"prettier": "^3.0.3",
"prettier-plugin-tailwindcss": "^0.5.6",
"remark-cli": "^11.0.0",
Expand Down
Loading