OpenAPI: Fix Waku compatibility & add example

This commit is contained in:
Fuma Nama
2025-08-01 19:00:22 +08:00
parent 9f67fb86e5
commit 6e29dd17dd
35 changed files with 626 additions and 35 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'fumadocs-openapi': patch
---
Fix Waku compatibility
-1
View File
@@ -1,7 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="ignoredErrors">
<list>
+19 -15
View File
@@ -4,10 +4,14 @@
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="edacdf1b-58dd-4ffe-9166-8e463dc8fa23" name="Changes" comment="re-create example">
<list default="true" id="edacdf1b-58dd-4ffe-9166-8e463dc8fa23" name="Changes" comment="CLI: Support `--no-git` option in CLI">
<change afterPath="$PROJECT_DIR$/examples/waku-openapi/scripts/generate-docs.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/inspectionProfiles/Project_Default.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/inspectionProfiles/Project_Default.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/packages/create-app/src/git.ts" beforeDir="false" afterPath="$PROJECT_DIR$/packages/create-app/src/git.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/packages/create-app/src/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/packages/create-app/src/index.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/packages/create-app/template/waku/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/packages/create-app/template/waku/package.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/packages/openapi/src/media/adapter.ts" beforeDir="false" afterPath="$PROJECT_DIR$/packages/openapi/src/media/adapter.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/packages/openapi/src/ui/lazy.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/packages/openapi/src/ui/lazy.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pnpm-lock.yaml" beforeDir="false" afterPath="$PROJECT_DIR$/pnpm-lock.yaml" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -691,15 +695,7 @@
<workItem from="1753864861092" duration="12502000" />
<workItem from="1753941132595" duration="6313000" />
<workItem from="1754034637804" duration="3426000" />
<workItem from="1754038073679" duration="735000" />
</task>
<task id="LOCAL-01760" summary="Docs: Improve AI guide">
<option name="closed" value="true" />
<created>1752923895663</created>
<option name="number" value="01760" />
<option name="presentableId" value="LOCAL-01760" />
<option name="project" value="LOCAL" />
<updated>1752923895663</updated>
<workItem from="1754038073679" duration="7877000" />
</task>
<task id="LOCAL-01761" summary="Docs: Improve AI guide">
<option name="closed" value="true" />
@@ -1085,7 +1081,15 @@
<option name="project" value="LOCAL" />
<updated>1754038108282</updated>
</task>
<option name="localTasksCounter" value="1809" />
<task id="LOCAL-01809" summary="CLI: Support `--no-git` option in CLI">
<option name="closed" value="true" />
<created>1754038849682</created>
<option name="number" value="01809" />
<option name="presentableId" value="LOCAL-01809" />
<option name="project" value="LOCAL" />
<updated>1754038849682</updated>
</task>
<option name="localTasksCounter" value="1810" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@@ -1136,7 +1140,6 @@
</component>
<component name="VcsManagerConfiguration">
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="false" />
<MESSAGE value="Docs: Rewrite Fumadocs MDX docs" />
<MESSAGE value="inline deps" />
<MESSAGE value="bump deps &amp; resolve conflicts" />
<MESSAGE value="Mixedbread integration (#2089)&#10;&#10;* feat: add mixedbread integration&#10;&#10;* chore(release): add changeset&#10;&#10;* docs: add mixedbread integration ui docs&#10;&#10;* feat: update tag filtering and extract title from chunked text&#10;&#10;* feat: improve extractHeadingTitle&#10;&#10;* fix: fix slug generation&#10;&#10;* feat: add a minor adjustment&#10;&#10;* feat: add adjustments&#10;&#10;* chore: make mixedbread sdk optional&#10;&#10;* feat: add metadata filtering&#10;&#10;* inline deps&#10;&#10;* Update sour-teeth-deny.md&#10;&#10;---------&#10;&#10;Co-authored-by: Fuma Nama &lt;[email protected]&gt;&#10;Co-authored-by: Fuma Nama &lt;[email protected]&gt;" />
@@ -1161,6 +1164,7 @@
<MESSAGE value="CLI: Add Waku template" />
<MESSAGE value="Merge remote-tracking branch 'refs/remotes/origin/dev' into dev" />
<MESSAGE value="re-create example" />
<option name="LAST_COMMIT_MESSAGE" value="re-create example" />
<MESSAGE value="CLI: Support `--no-git` option in CLI" />
<option name="LAST_COMMIT_MESSAGE" value="CLI: Support `--no-git` option in CLI" />
</component>
</project>
+6
View File
@@ -0,0 +1,6 @@
.DS_Store
/node_modules/
dist
source.generated.ts
src/pages.gen.ts
@@ -0,0 +1,15 @@
---
title: Create a celestial body
full: true
_openapi:
method: POST
route: /celestial-bodies
toc: []
structuredData:
headings: []
contents: []
---
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
<APIPage document={"https://cdn.jsdelivr.net/npm/@scalar/galaxy/dist/latest.yaml"} operations={[{"path":"/celestial-bodies","method":"post"}]} webhooks={[]} hasHead={false} />
@@ -0,0 +1,20 @@
---
title: Create a planet
full: true
_openapi:
method: POST
route: /planets
toc: []
structuredData:
headings: []
contents:
- content: >-
Time to play god and create a new planet. What do you think? Ah, don't
think too much. What could go wrong anyway?
---
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
Time to play god and create a new planet. What do you think? Ah, don't think too much. What could go wrong anyway?
<APIPage document={"https://cdn.jsdelivr.net/npm/@scalar/galaxy/dist/latest.yaml"} operations={[{"path":"/planets","method":"post"}]} webhooks={[]} hasHead={false} />
@@ -0,0 +1,18 @@
---
title: Create a user
full: true
_openapi:
method: POST
route: /user/signup
toc: []
structuredData:
headings: []
contents:
- content: Time to create a user account, eh?
---
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
Time to create a user account, eh?
<APIPage document={"https://cdn.jsdelivr.net/npm/@scalar/galaxy/dist/latest.yaml"} operations={[{"path":"/user/signup","method":"post"}]} webhooks={[]} hasHead={false} />
@@ -0,0 +1,21 @@
---
title: Delete a planet
full: true
_openapi:
method: DELETE
route: /planets/{planetId}
toc: []
structuredData:
headings: []
contents:
- content: >-
This endpoint was used to delete planets. Unfortunately, that caused a
lot of trouble for planets with life. So, this endpoint is now
deprecated and should not be used anymore.
---
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
This endpoint was used to delete planets. Unfortunately, that caused a lot of trouble for planets with life. So, this endpoint is now deprecated and should not be used anymore.
<APIPage document={"https://cdn.jsdelivr.net/npm/@scalar/galaxy/dist/latest.yaml"} operations={[{"path":"/planets/{planetId}","method":"delete"}]} webhooks={[]} hasHead={false} />
@@ -0,0 +1,20 @@
---
title: Get all planets
full: true
_openapi:
method: GET
route: /planets
toc: []
structuredData:
headings: []
contents:
- content: >-
It's easy to say you know them all, but do you really? Retrieve all
the planets and check whether you missed one.
---
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
It's easy to say you know them all, but do you really? Retrieve all the planets and check whether you missed one.
<APIPage document={"https://cdn.jsdelivr.net/npm/@scalar/galaxy/dist/latest.yaml"} operations={[{"path":"/planets","method":"get"}]} webhooks={[]} hasHead={false} />
@@ -0,0 +1,18 @@
---
title: Get authenticated user
full: true
_openapi:
method: GET
route: /me
toc: []
structuredData:
headings: []
contents:
- content: Find yourself they say. That's what you can do here.
---
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
Find yourself they say. That's what you can do here.
<APIPage document={"https://cdn.jsdelivr.net/npm/@scalar/galaxy/dist/latest.yaml"} operations={[{"path":"/me","method":"get"}]} webhooks={[]} hasHead={false} />
@@ -0,0 +1,20 @@
---
title: Get a planet
full: true
_openapi:
method: GET
route: /planets/{planetId}
toc: []
structuredData:
headings: []
contents:
- content: >-
You'll better learn a little bit more about the planets. It might come
in handy once space travel is available for everyone.
---
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
You'll better learn a little bit more about the planets. It might come in handy once space travel is available for everyone.
<APIPage document={"https://cdn.jsdelivr.net/npm/@scalar/galaxy/dist/latest.yaml"} operations={[{"path":"/planets/{planetId}","method":"get"}]} webhooks={[]} hasHead={false} />
@@ -0,0 +1,20 @@
---
title: Get a token
full: true
_openapi:
method: POST
route: /auth/token
toc: []
structuredData:
headings: []
contents:
- content: >-
Yeah, this is the boring security stuff. Just get your super secret
token and move on.
---
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
Yeah, this is the boring security stuff. Just get your super secret token and move on.
<APIPage document={"https://cdn.jsdelivr.net/npm/@scalar/galaxy/dist/latest.yaml"} operations={[{"path":"/auth/token","method":"post"}]} webhooks={[]} hasHead={false} />
@@ -0,0 +1,31 @@
---
title: Hello World
description: Your favourite docs framework.
icon: Rocket
---
Hey there! Fumadocs is a docs framework that works on Waku!
## Heading
Hello World!
<Cards>
<Card title="Learn more about Waku" href="https://waku.gg" />
<Card title="Learn more about Fumadocs" href="https://fumadocs.vercel.app" />
</Cards>
### CodeBlock
```ts
console.log('Hello World');
```
#### Table
| Head | Description |
| ------------------------------- | ----------------------------------- |
| `hello` | Hello World |
| very **important** | Hey |
| _Surprisingly_ | Fumadocs |
| very long text that looks weird | hello world hello world hello world |
@@ -0,0 +1,13 @@
---
title: New Planet
full: true
_openapi:
toc: []
structuredData:
headings: []
contents: []
---
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
<APIPage document={"https://cdn.jsdelivr.net/npm/@scalar/galaxy/dist/latest.yaml"} operations={[]} webhooks={[{"name":"newPlanet","method":"post"}]} hasHead={false} />
@@ -0,0 +1,12 @@
---
title: Test
description: This is another page
---
Hello World again!
## Installation
```npm
npm i fumadocs-core fumadocs-ui
```
@@ -0,0 +1,20 @@
---
title: Update a planet
full: true
_openapi:
method: PUT
route: /planets/{planetId}
toc: []
structuredData:
headings: []
contents:
- content: >-
Sometimes you make mistakes, that's fine. No worries, you can update
all planets.
---
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
Sometimes you make mistakes, that's fine. No worries, you can update all planets.
<APIPage document={"https://cdn.jsdelivr.net/npm/@scalar/galaxy/dist/latest.yaml"} operations={[{"path":"/planets/{planetId}","method":"put"}]} webhooks={[]} hasHead={false} />
@@ -0,0 +1,18 @@
---
title: Upload an image to a planet
full: true
_openapi:
method: POST
route: /planets/{planetId}/image
toc: []
structuredData:
headings: []
contents:
- content: Got a crazy good photo of a planet? Share it with the world!
---
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
Got a crazy good photo of a planet? Share it with the world!
<APIPage document={"https://cdn.jsdelivr.net/npm/@scalar/galaxy/dist/latest.yaml"} operations={[{"path":"/planets/{planetId}/image","method":"post"}]} webhooks={[]} hasHead={false} />
+32
View File
@@ -0,0 +1,32 @@
{
"name": "example-waku-openapi",
"private": true,
"sideEffects": false,
"type": "module",
"scripts": {
"dev": "waku dev",
"build": "waku build",
"start": "waku start",
"build:docs": "bun ./scripts/generate-docs.ts"
},
"dependencies": {
"fumadocs-core": "workspace:*",
"fumadocs-mdx": "workspace:*",
"fumadocs-openapi": "workspace:*",
"fumadocs-ui": "workspace:*",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-server-dom-webpack": "^19.1.1",
"waku": "^0.23.6"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.1.11",
"postcss": "^8.5.6",
"@types/node": "^24.1.0",
"@types/react": "^19.1.9",
"@types/react-dom": "^19.1.7",
"tailwindcss": "^4.1.11",
"typescript": "^5.7.2",
"vite": "7.0.5"
}
}
+5
View File
@@ -0,0 +1,5 @@
export default {
plugins: {
'@tailwindcss/postcss': {},
},
};
@@ -0,0 +1,7 @@
import { generateFiles } from 'fumadocs-openapi';
void generateFiles({
input: ['https://cdn.jsdelivr.net/npm/@scalar/galaxy/dist/latest.yaml'],
output: './content/docs',
includeDescription: true,
});
+7
View File
@@ -0,0 +1,7 @@
import { defineConfig, defineDocs } from 'fumadocs-mdx/config';
export const docs = defineDocs({
dir: 'content/docs',
});
export default defineConfig();
@@ -0,0 +1,12 @@
'use client';
import type { ReactNode } from 'react';
import { WakuProvider } from 'fumadocs-core/framework/waku';
import { RootProvider } from 'fumadocs-ui/provider/base';
export function Provider({ children }: { children: ReactNode }) {
return (
<WakuProvider>
<RootProvider>{children}</RootProvider>
</WakuProvider>
);
}
@@ -0,0 +1,18 @@
import '../styles/globals.css';
import type { ReactNode } from 'react';
import { Provider } from '../components/provider';
export default function RootLayout({
children,
}: {
children: ReactNode;
path: string;
}) {
return <Provider>{children}</Provider>;
}
export const getConfig = async () => {
return {
render: 'static',
};
};
@@ -0,0 +1,4 @@
import { createFromSource } from 'fumadocs-core/search/server';
import { source } from '../../source';
export const { GET } = createFromSource(source);
@@ -0,0 +1,52 @@
import { openapi, source } from '../../source';
import { PageProps } from 'waku/router';
import defaultMdxComponents from 'fumadocs-ui/mdx';
import {
DocsBody,
DocsDescription,
DocsPage,
DocsTitle,
} from 'fumadocs-ui/page';
import { APIPage } from 'fumadocs-openapi/ui';
export default function DocPage({ slugs }: PageProps<'/docs/[...slugs]'>) {
const page = source.getPage(slugs);
if (!page) {
return (
<div className="text-center py-12">
<h1 className="text-3xl font-bold mb-4 text-gray-900 dark:text-gray-100">
Page Not Found
</h1>
<p className="text-gray-600 dark:text-gray-400">
The page you are looking for does not exist.
</p>
</div>
);
}
const MDX = page.data.default;
return (
<DocsPage toc={page.data.toc}>
<DocsTitle>{page.data.title}</DocsTitle>
<DocsDescription>{page.data.description}</DocsDescription>
<DocsBody>
<MDX
components={{
...defaultMdxComponents,
APIPage: (props) => <APIPage {...openapi.getAPIPageProps(props)} />,
}}
/>
</DocsBody>
</DocsPage>
);
}
export async function getConfig() {
const pages = source.getPages().map((page) => page.slugs);
return {
render: 'static' as const,
staticPaths: pages,
} as const;
}
@@ -0,0 +1,27 @@
import type { ReactNode } from 'react';
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import { source } from '../../source';
export default function Layout({
children,
}: {
children: ReactNode;
path: string;
}) {
return (
<DocsLayout
tree={source.pageTree}
nav={{
title: 'Waku',
}}
>
{children}
</DocsLayout>
);
}
export const getConfig = async () => {
return {
render: 'static',
};
};
+27
View File
@@ -0,0 +1,27 @@
import { HomeLayout } from 'fumadocs-ui/layouts/home';
import { Link } from 'waku';
export default function Home() {
return (
<HomeLayout
nav={{
title: 'Waku',
}}
className="text-center py-32 justify-center"
>
<h1 className="font-medium text-xl mb-4">Fumadocs on Waku.</h1>
<Link
to="/docs"
className="px-3 py-2 rounded-lg bg-fd-primary text-fd-primary-foreground font-medium text-sm mx-auto"
>
Open Docs
</Link>
</HomeLayout>
);
}
export const getConfig = async () => {
return {
render: 'static',
};
};
+13
View File
@@ -0,0 +1,13 @@
import { loader } from 'fumadocs-core/source';
import { create, docs } from '../source.generated.js';
import { attachFile, createOpenAPI } from 'fumadocs-openapi/server';
export const source = loader({
source: await create.sourceAsync(docs.doc, docs.meta),
baseUrl: '/docs',
pageTree: {
attachFile,
},
});
export const openapi = createOpenAPI();
@@ -0,0 +1,10 @@
@import 'tailwindcss';
@import 'fumadocs-ui/css/neutral.css';
@import 'fumadocs-ui/css/preset.css';
@import 'fumadocs-openapi/css/preset.css';
body {
display: flex;
flex-direction: column;
min-height: 100vh;
}
+19
View File
@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": ["dom", "dom.iterable", "ES2022"],
"jsx": "react-jsx",
"module": "ESNext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"noUncheckedIndexedAccess": true,
"noEmit": true
},
"exclude": ["node_modules", "dist"]
}
+14
View File
@@ -0,0 +1,14 @@
import { defineConfig } from 'waku/config';
import mdx from 'fumadocs-mdx/vite';
import * as MdxConfig from './source.config.js';
import type { UserConfig } from 'vite';
export default defineConfig({
unstable_viteConfigs: {
common() {
return {
plugins: [mdx(MdxConfig)],
} as UserConfig;
},
},
});
@@ -22,6 +22,8 @@
"@types/react": "^19.1.9",
"@types/react-dom": "^19.1.7",
"tailwindcss": "^4.1.11",
"@tailwindcss/postcss": "^4.1.11",
"postcss": "^8.5.6",
"typescript": "^5.7.2",
"vite": "7.0.5"
}
+1 -1
View File
@@ -1,6 +1,6 @@
import { escapeString, inputToString } from '@/utils/input-to-string';
// @ts-expect-error -- untyped
import { js2xml } from 'xml-js/lib/js2xml';
import js2xml from 'xml-js/lib/js2xml';
interface BaseContext {
/**
+42 -18
View File
@@ -1,24 +1,48 @@
'use client';
import { lazy } from 'react';
import {
type ComponentProps,
type ComponentType,
lazy,
type LazyExoticComponent,
} from 'react';
export const CodeExampleProvider = lazy(() =>
import('./contexts/code-example').then((mod) => ({
default: mod.CodeExampleProvider,
})),
);
export const CodeExample = lazy(() =>
import('./contexts/code-example').then((mod) => ({
default: mod.CodeExample,
})),
);
export const CodeExampleSelector = lazy(() =>
import('./contexts/code-example').then((mod) => ({
default: mod.CodeExampleSelector,
})),
// Waku wraps all export functions of a "use client" file in `React.lazy`, but `lazy(lazy(() => ...))` causes error.
// we wrap another layer of component such that it is valid
// TODO: perhaps we can remove it once Waku migrated to vite-rsc
function wrap<C extends LazyExoticComponent<ComponentType<any>>>(V: C) {
return function wrapper(props: ComponentProps<C>) {
return <V {...props} />;
};
}
export const CodeExampleProvider = wrap(
lazy(() =>
import('./contexts/code-example').then((mod) => ({
default: mod.CodeExampleProvider,
})),
),
);
export const ClientLazy = lazy(() => import('@/playground/client'));
export const CodeExample = wrap(
lazy(() =>
import('./contexts/code-example').then((mod) => ({
default: mod.CodeExample,
})),
),
);
export const ApiProvider = lazy(() =>
import('./contexts/api').then((mod) => ({ default: mod.ApiProvider })),
export const CodeExampleSelector = wrap(
lazy(() =>
import('./contexts/code-example').then((mod) => ({
default: mod.CodeExampleSelector,
})),
),
);
export const ClientLazy = wrap(lazy(() => import('@/playground/client')));
export const ApiProvider = wrap(
lazy(() =>
import('./contexts/api').then((mod) => ({ default: mod.ApiProvider })),
),
);
+58
View File
@@ -724,6 +724,9 @@ importers:
specifier: ^0.23.6
version: 0.23.6(@swc/[email protected])(@types/[email protected])(@types/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]([email protected]([email protected]))([email protected])([email protected](@swc/[email protected])([email protected])))([email protected])([email protected])([email protected])([email protected])
devDependencies:
'@tailwindcss/postcss':
specifier: ^4.1.11
version: 4.1.11
'@types/node':
specifier: ^24.1.0
version: 24.1.0
@@ -733,6 +736,61 @@ importers:
'@types/react-dom':
specifier: ^19.1.7
version: 19.1.7(@types/[email protected])
postcss:
specifier: ^8.5.6
version: 8.5.6
tailwindcss:
specifier: ^4.1.11
version: 4.1.11
typescript:
specifier: ^5.7.2
version: 5.8.3
vite:
specifier: 7.0.5
version: 7.0.5(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
examples/waku-openapi:
dependencies:
fumadocs-core:
specifier: workspace:*
version: link:../../packages/core
fumadocs-mdx:
specifier: workspace:*
version: link:../../packages/mdx
fumadocs-openapi:
specifier: workspace:*
version: link:../../packages/openapi
fumadocs-ui:
specifier: workspace:*
version: link:../../packages/ui
react:
specifier: ^19.1.1
version: 19.1.1
react-dom:
specifier: ^19.1.1
version: 19.1.1([email protected])
react-server-dom-webpack:
specifier: ^19.1.1
version: 19.1.1([email protected]([email protected]))([email protected])([email protected](@swc/[email protected])([email protected]))
waku:
specifier: ^0.23.6
version: 0.23.6(@swc/[email protected])(@types/[email protected])(@types/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]([email protected]([email protected]))([email protected])([email protected](@swc/[email protected])([email protected])))([email protected])([email protected])([email protected])([email protected])
devDependencies:
'@tailwindcss/postcss':
specifier: ^4.1.11
version: 4.1.11
'@types/node':
specifier: ^24.1.0
version: 24.1.0
'@types/react':
specifier: ^19.1.9
version: 19.1.9
'@types/react-dom':
specifier: ^19.1.7
version: 19.1.7(@types/[email protected])
postcss:
specifier: ^8.5.6
version: 8.5.6
tailwindcss:
specifier: ^4.1.11
version: 4.1.11