diff --git a/docs/suspensive.org/next.config.mjs b/docs/suspensive.org/next.config.mjs index e9c143222..396e85866 100644 --- a/docs/suspensive.org/next.config.mjs +++ b/docs/suspensive.org/next.config.mjs @@ -1,6 +1,14 @@ +import { recmaCodeHike, remarkCodeHike } from 'codehike/mdx' import nextra from 'nextra' import { remarkSandpack } from 'remark-sandpack' +/** @type {import('codehike/mdx').CodeHikeConfig} */ +const chConfig = { + syntaxHighlighting: { + theme: 'github-dark', + }, +} + const withNextra = nextra({ autoImportThemeStyle: true, theme: 'nextra-theme-docs', @@ -8,7 +16,8 @@ const withNextra = nextra({ defaultShowCopyCode: true, latex: true, mdxOptions: { - remarkPlugins: [remarkSandpack], + remarkPlugins: [[remarkCodeHike, chConfig], remarkSandpack], + recmaPlugins: [[recmaCodeHike, chConfig]], rehypePlugins: [], rehypePrettyCodeOptions: { theme: 'github-dark-default', diff --git a/docs/suspensive.org/package.json b/docs/suspensive.org/package.json index e1a0fa834..b27c8b5a7 100644 --- a/docs/suspensive.org/package.json +++ b/docs/suspensive.org/package.json @@ -27,6 +27,7 @@ "@suspensive/react-query-4": "workspace:*", "@tanstack/react-query": "^4.36.1", "@tanstack/react-query-devtools": "^4.36.1", + "codehike": "^1.0.4", "d3": "^7.9.0", "framer-motion": "^11.11.8", "next": "catalog:", @@ -35,7 +36,8 @@ "react": "catalog:react18", "react-dom": "catalog:react18", "remark-sandpack": "^0.0.5", - "sharp": "catalog:" + "sharp": "catalog:", + "zod": "^3.23.8" }, "devDependencies": { "@suspensive/eslint-config": "workspace:*", diff --git a/docs/suspensive.org/src/components/Scrollycoding.tsx b/docs/suspensive.org/src/components/Scrollycoding.tsx new file mode 100644 index 000000000..e5fc5b9ff --- /dev/null +++ b/docs/suspensive.org/src/components/Scrollycoding.tsx @@ -0,0 +1,139 @@ +import { Block, HighlightedCodeBlock, parseProps } from 'codehike/blocks' +import { + type AnnotationHandler, + type CustomPreProps, + type HighlightedCode, + InnerLine, + InnerPre, + InnerToken, + Pre, + getPreRef, +} from 'codehike/code' +import { + Selectable, + Selection, + SelectionProvider, +} from 'codehike/utils/selection' +import { + type TokenTransitionsSnapshot, + calculateTransitions, + getStartingSnapshot, +} from 'codehike/utils/token-transitions' +import { Component, type RefObject } from 'react' +import { z } from 'zod' + +const MAX_TRANSITION_DURATION = 900 // milliseconds + +export class SmoothPre extends Component { + ref: RefObject + constructor(props: CustomPreProps) { + super(props) + this.ref = getPreRef(this.props) + } + + render() { + return + } + + getSnapshotBeforeUpdate() { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return getStartingSnapshot(this.ref.current!) + } + + componentDidUpdate( + prevProps: never, + prevState: never, + snapshot: TokenTransitionsSnapshot + ) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const transitions = calculateTransitions(this.ref.current!, snapshot) + transitions.forEach(({ element, keyframes, options }) => { + const { translateX, translateY, ...kf } = keyframes as any + if (translateX && translateY) { + kf.translate = [ + `${translateX[0]}px ${translateY[0]}px`, + `${translateX[1]}px ${translateY[1]}px`, + ] + } + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + element.animate(kf, { + duration: options.duration * MAX_TRANSITION_DURATION, + delay: options.delay * MAX_TRANSITION_DURATION, + easing: options.easing, + fill: 'both', + }) + }) + } +} + +const Schema = Block.extend({ + steps: z.array(Block.extend({ code: HighlightedCodeBlock })), +}) + +export function Scrollycoding(props: unknown) { + const { steps } = parseProps(props, Schema) + return ( + +
+ {steps.map((step, i) => ( + +

+ {step.title} +

+
{step.children}
+
+ ))} +
+ +
+
+ ( + // eslint-disable-next-line @eslint-react/no-duplicate-key + + ))} + /> +
+
+
+ ) +} + +const tokenTransitions: AnnotationHandler = { + name: 'token-transitions', + PreWithRef: SmoothPre, + Token: (props) => ( + + ), +} +const wordWrap: AnnotationHandler = { + name: 'word-wrap', + Pre: (props) => , + Line: (props) => ( + + ), + Token: (props) => , +} +function Code({ codeblock }: { codeblock: HighlightedCode }) { + return ( +
+  )
+}
diff --git a/docs/suspensive.org/src/components/index.ts b/docs/suspensive.org/src/components/index.ts
index b14bc4374..0e7eee0cc 100644
--- a/docs/suspensive.org/src/components/index.ts
+++ b/docs/suspensive.org/src/components/index.ts
@@ -2,3 +2,4 @@ export { Callout } from './Callout'
 export { HomePage } from './HomePage'
 export { Sandpack } from './Sandpack'
 export { BubbleChart } from './BubbleChart'
+export { Scrollycoding } from './Scrollycoding'
diff --git a/docs/suspensive.org/src/pages/en/index.mdx b/docs/suspensive.org/src/pages/en/index.mdx
index 32d45e79d..b40bcebee 100644
--- a/docs/suspensive.org/src/pages/en/index.mdx
+++ b/docs/suspensive.org/src/pages/en/index.mdx
@@ -1,9 +1,9 @@
-import { HomePage } from '@/components'
+import { HomePage, Scrollycoding } from '@/components'
 
 
+
+
+
+# !!steps 대표적인 라이브러리인 TanStack Query로 Suspense 없이 코드를 작성한다면 이렇게 작성합니다.
+
+이 경우 isLoading과 isError를 체크하여 로딩과 에러 상태를 처리하고 타입스크립트적으로 data에서 undefined를 제거할 수 있습니다.
+
+```jsx ! Page.jsx
+const Page = () => {
+  const userQuery = useQuery(userQueryOptions())
+  const postsQuery = useQuery({
+    ...postsQueryOptions(),
+    select: (posts) => posts.filter(({ isPublic }) => isPublic),
+  })
+  const promotionsQuery = useQuery(promotionsQueryOptions())
+
+  if (
+    userQuery.isLoading ||
+    postsQuery.isLoading ||
+    promotionsQuery.isLoading
+  ) {
+    return 'loading...'
+  }
+
+  if (userQuery.isError || postsQuery.isError || promotionsQuery.isError) {
+    return 'error'
+  }
+
+  return (
+    
+      
+      {postsQuery.data.map((post) => (
+        
+      ))}
+      {promotionsQuery.data.map((promotion) => (
+        
+      ))}
+    
+  )
+}
+```
+
+# !!steps 그런데 만약 조회해야 할 api가 더 많아진다고 가정해봅시다.
+
+조회해야 하는 api가 더 많아진다면 이 로딩상태와 에러상태를 처리하는 코드가 더욱 복잡해집니다.
+
+```jsx ! Page.jsx
+const Page = () => {
+  const userQuery = useQuery(userQueryOptions())
+  const postsQuery = useQuery({
+    ...postsQueryOptions(),
+    select: (posts) => posts.filter(({ isPublic }) => isPublic),
+  })
+  const promotionsQuery = useQuery(promotionsQueryOptions())
+  // 이 부분이 늘어날수록 코드가 복잡해집니다.
+
+  if (
+    userQuery.isLoading ||
+    postsQuery.isLoading ||
+    promotionsQuery.isLoading // 이 부분에서 매번 추가해야 합니다.
+  ) {
+    return 'loading...'
+  }
+
+  if (
+    userQuery.isError ||
+    postsQuery.isError ||
+    promotionsQuery.isError // 이 부분에서 매번 추가해야 합니다.
+  ) {
+    return 'error'
+  }
+
+  return (
+    
+      
+      {postsQuery.data.map((post) => (
+        
+      ))}
+      {promotionsQuery.data.map((promotion) => (
+        
+      ))}
+      {/* 이 부분에서 매번 추가해야 합니다. */}
+    
+  )
+}
+```
+
+# !!steps Suspense를 사용하면 타입적으로 코드가 간결해집니다. 하지만 컴포넌트의 깊이는 깊어질 수 밖에 없습니다.
+
+useSuspenseQuery는 Suspense와 ErrorBoundary를 사용하여 외부에서 로딩과 에러 상태를 처리할 수 있습니다.
+하지만 useSuspenseQuery는 hook이기 때문에 부모에 Suspense와 ErrorBoundary를 두기 위해 컴포넌트가 분리되어야만 하기 때문에 뎁스가 깊어지는 문제가 있습니다.
+
+```jsx ! Page.jsx
+const Page = () => (
+  
+    
+      
+      
+      
+    
+  
+)
+
+const UserInfo = ({ userId }) => {
+  const { data: user } = useSuspenseQuery(userQueryOptions())
+  return 
+}
+
+const PostList = ({ userId }) => {
+  const { data: posts } = useSuspenseQuery({
+    ...postsQueryOptions(),
+    select: (posts) => posts.filter(({ isPublic }) => isPublic),
+  })
+  return posts.map((post) => )
+}
+
+const PromotionList = ({ userId }) => {
+  const { data: promotions } = useSuspenseQuery(promotionsQueryOptions())
+  return promotions.map((promotion) => (
+    
+  ))
+}
+```
+
+# !!steps Suspensive의 SuspenseQuery 컴포넌트를 사용하면 hook의 제약을 피해 같은 뎁스에서 더욱 쉽게 코드를 작성할 수 있습니다.
+
+1. SuspenseQuery를 사용하면 depth를 제거할 수 있습니다.
+2. UserInfo라는 컴포넌트를 제거하고 UserProfile과 같은 Presentational 컴포넌트만 남으므로 테스트하기 쉬워집니다.
+
+```jsx ! Page.jsx
+const Page = () => (
+  
+    
+      
+        {({ data: user }) => }
+      
+       posts.filter(({ isPublic }) => isPublic)}
+      >
+        {({ data: posts }) =>
+          posts.map((post) => )
+        }
+      
+       promotions.filter(({ isPublic }) => isPublic)}
+      >
+        {({ data: promotions }) =>
+          promotions.map((promotion) => (
+            
+          ))
+        }
+      
+    
+  
+)
+```
+
+
+
+# 이것이 우리가 Suspensive를 만드는 이유입니다.
+
+더 쉬운 React Suspense를 사용하세요
+
+## ErrorBoundaryGroup
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index fb5b63b01..ba5f513fd 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -259,6 +259,9 @@ importers:
       '@tanstack/react-query-devtools':
         specifier: ^4.36.1
         version: 4.36.1(@tanstack/react-query@4.36.1(react-dom@18.3.1(react@18.3.1))(react-native@0.76.1(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      codehike:
+        specifier: ^1.0.4
+        version: 1.0.4
       d3:
         specifier: ^7.9.0
         version: 7.9.0
@@ -286,6 +289,9 @@ importers:
       sharp:
         specifier: 'catalog:'
         version: 0.33.5
+      zod:
+        specifier: ^3.23.8
+        version: 3.23.8
     devDependencies:
       '@suspensive/eslint-config':
         specifier: workspace:*
@@ -1642,6 +1648,9 @@ packages:
   '@chevrotain/utils@11.0.3':
     resolution: {integrity: sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==}
 
+  '@code-hike/lighter@1.0.1':
+    resolution: {integrity: sha512-mccvcsk5UTScRrE02oBz1/qzckyhD8YE3VQlQv++2bSVVZgNuCUX8MpokSCi5OmfRAAxbj6kmNiqq1Um8eXPrw==}
+
   '@codemirror/autocomplete@6.18.3':
     resolution: {integrity: sha512-1dNIOmiM0z4BIBwxmxEfA1yoxh1MF/6KPBbh20a5vphGV0ictKlgQsbJs6D6SkR6iJpGbpwRsa6PFMNlg9T9pQ==}
     peerDependencies:
@@ -2352,7 +2361,7 @@ packages:
 
   '@expo/bunyan@4.0.1':
     resolution: {integrity: sha512-+Lla7nYSiHZirgK+U/uYzsLv/X+HaJienbD5AKX1UQZHYfWaP+9uuQluRB4GrEVWF0GZ7vEVp/jzaOT9k/SQlg==}
-    engines: {node: '>=0.10.0'}
+    engines: {'0': node >=0.10.0}
 
   '@expo/cli@0.21.3':
     resolution: {integrity: sha512-r7MUilXmvtJ3inxD1P+uefTmVrIV43QLZlG0YvHPGX2NGCZcFFXwZkSquqnfZ2gzOXdHs2cAbqLJsH37nDq14g==}
@@ -2511,67 +2520,79 @@ packages:
     resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==}
     cpu: [arm64]
     os: [linux]
+    libc: [glibc]
 
   '@img/sharp-libvips-linux-arm@1.0.5':
     resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==}
     cpu: [arm]
     os: [linux]
+    libc: [glibc]
 
   '@img/sharp-libvips-linux-s390x@1.0.4':
     resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==}
     cpu: [s390x]
     os: [linux]
+    libc: [glibc]
 
   '@img/sharp-libvips-linux-x64@1.0.4':
     resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==}
     cpu: [x64]
     os: [linux]
+    libc: [glibc]
 
   '@img/sharp-libvips-linuxmusl-arm64@1.0.4':
     resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==}
     cpu: [arm64]
     os: [linux]
+    libc: [musl]
 
   '@img/sharp-libvips-linuxmusl-x64@1.0.4':
     resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==}
     cpu: [x64]
     os: [linux]
+    libc: [musl]
 
   '@img/sharp-linux-arm64@0.33.5':
     resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==}
     engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
     cpu: [arm64]
     os: [linux]
+    libc: [glibc]
 
   '@img/sharp-linux-arm@0.33.5':
     resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==}
     engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
     cpu: [arm]
     os: [linux]
+    libc: [glibc]
 
   '@img/sharp-linux-s390x@0.33.5':
     resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==}
     engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
     cpu: [s390x]
     os: [linux]
+    libc: [glibc]
 
   '@img/sharp-linux-x64@0.33.5':
     resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==}
     engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
     cpu: [x64]
     os: [linux]
+    libc: [glibc]
 
   '@img/sharp-linuxmusl-arm64@0.33.5':
     resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==}
     engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
     cpu: [arm64]
     os: [linux]
+    libc: [musl]
 
   '@img/sharp-linuxmusl-x64@0.33.5':
     resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==}
     engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
     cpu: [x64]
     os: [linux]
+    libc: [musl]
 
   '@img/sharp-wasm32@0.33.5':
     resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==}
@@ -2801,36 +2822,42 @@ packages:
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [linux]
+    libc: [glibc]
 
   '@napi-rs/simple-git-linux-arm64-musl@0.1.19':
     resolution: {integrity: sha512-OwTRF+H4IZYxmDFRi1IrLMfqbdIpvHeYbJl2X94NVsLVOY+3NUHvEzL3fYaVx5urBaMnIK0DD3wZLbcueWvxbA==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [linux]
+    libc: [musl]
 
   '@napi-rs/simple-git-linux-powerpc64le-gnu@0.1.19':
     resolution: {integrity: sha512-p7zuNNVyzpRvkCt2RIGv9FX/WPcPbZ6/FRUgUTZkA2WU33mrbvNqSi4AOqCCl6mBvEd+EOw5NU4lS9ORRJvAEg==}
     engines: {node: '>= 10'}
     cpu: [powerpc64le]
     os: [linux]
+    libc: [glibc]
 
   '@napi-rs/simple-git-linux-s390x-gnu@0.1.19':
     resolution: {integrity: sha512-6N2vwJUPLiak8GLrS0a3is0gSb0UwI2CHOOqtvQxPmv+JVI8kn3vKiUscsktdDb0wGEPeZ8PvZs0y8UWix7K4g==}
     engines: {node: '>= 10'}
     cpu: [s390x]
     os: [linux]
+    libc: [glibc]
 
   '@napi-rs/simple-git-linux-x64-gnu@0.1.19':
     resolution: {integrity: sha512-61YfeO1J13WK7MalLgP3QlV6of2rWnVw1aqxWkAgy/lGxoOFSJ4Wid6ANVCEZk4tJpPX/XNeneqkUz5xpeb2Cw==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [linux]
+    libc: [glibc]
 
   '@napi-rs/simple-git-linux-x64-musl@0.1.19':
     resolution: {integrity: sha512-cCTWNpMJnN3PrUBItWcs3dQKCydsIasbrS3laMzq8k7OzF93Zrp2LWDTPlLCO9brbBVpBzy2Qk5Xg9uAfe/Ukw==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [linux]
+    libc: [musl]
 
   '@napi-rs/simple-git-win32-arm64-msvc@0.1.19':
     resolution: {integrity: sha512-sWavb1BjeLKKBA+PbTsRSSzVNfb7V/dOpaJvkgR5d2kWFn/AHmCZHSSj/3nyZdYf0BdDC+DIvqk3daAEZ6QMVw==}
@@ -2871,24 +2898,28 @@ packages:
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [linux]
+    libc: [glibc]
 
   '@next/swc-linux-arm64-musl@15.0.3':
     resolution: {integrity: sha512-WkAk6R60mwDjH4lG/JBpb2xHl2/0Vj0ZRu1TIzWuOYfQ9tt9NFsIinI1Epma77JVgy81F32X/AeD+B2cBu/YQA==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [linux]
+    libc: [musl]
 
   '@next/swc-linux-x64-gnu@15.0.3':
     resolution: {integrity: sha512-gWL/Cta1aPVqIGgDb6nxkqy06DkwJ9gAnKORdHWX1QBbSZZB+biFYPFti8aKIQL7otCE1pjyPaXpFzGeG2OS2w==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [linux]
+    libc: [glibc]
 
   '@next/swc-linux-x64-musl@15.0.3':
     resolution: {integrity: sha512-QQEMwFd8r7C0GxQS62Zcdy6GKx999I/rTO2ubdXEe+MlZk9ZiinsrjwoiBL5/57tfyjikgh6GOU2WRQVUej3UA==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [linux]
+    libc: [musl]
 
   '@next/swc-win32-arm64-msvc@15.0.3':
     resolution: {integrity: sha512-9TEp47AAd/ms9fPNgtgnT7F3M1Hf7koIYYWCMQ9neOwjbVWJsHZxrFbI3iEDJ8rf1TDGpmHbKxXf2IFpAvheIQ==}
@@ -3211,46 +3242,55 @@ packages:
     resolution: {integrity: sha512-paHF1bMXKDuizaMODm2bBTjRiHxESWiIyIdMugKeLnjuS1TCS54MF5+Y5Dx8Ui/1RBPVRE09i5OUlaLnv8OGnA==}
     cpu: [arm]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-arm-musleabihf@4.26.0':
     resolution: {integrity: sha512-cwxiHZU1GAs+TMxvgPfUDtVZjdBdTsQwVnNlzRXC5QzIJ6nhfB4I1ahKoe9yPmoaA/Vhf7m9dB1chGPpDRdGXg==}
     cpu: [arm]
     os: [linux]
+    libc: [musl]
 
   '@rollup/rollup-linux-arm64-gnu@4.26.0':
     resolution: {integrity: sha512-4daeEUQutGRCW/9zEo8JtdAgtJ1q2g5oHaoQaZbMSKaIWKDQwQ3Yx0/3jJNmpzrsScIPtx/V+1AfibLisb3AMQ==}
     cpu: [arm64]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-arm64-musl@4.26.0':
     resolution: {integrity: sha512-eGkX7zzkNxvvS05ROzJ/cO/AKqNvR/7t1jA3VZDi2vRniLKwAWxUr85fH3NsvtxU5vnUUKFHKh8flIBdlo2b3Q==}
     cpu: [arm64]
     os: [linux]
+    libc: [musl]
 
   '@rollup/rollup-linux-powerpc64le-gnu@4.26.0':
     resolution: {integrity: sha512-Odp/lgHbW/mAqw/pU21goo5ruWsytP7/HCC/liOt0zcGG0llYWKrd10k9Fj0pdj3prQ63N5yQLCLiE7HTX+MYw==}
     cpu: [ppc64]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-riscv64-gnu@4.26.0':
     resolution: {integrity: sha512-MBR2ZhCTzUgVD0OJdTzNeF4+zsVogIR1U/FsyuFerwcqjZGvg2nYe24SAHp8O5sN8ZkRVbHwlYeHqcSQ8tcYew==}
     cpu: [riscv64]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-s390x-gnu@4.26.0':
     resolution: {integrity: sha512-YYcg8MkbN17fMbRMZuxwmxWqsmQufh3ZJFxFGoHjrE7bv0X+T6l3glcdzd7IKLiwhT+PZOJCblpnNlz1/C3kGQ==}
     cpu: [s390x]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-x64-gnu@4.26.0':
     resolution: {integrity: sha512-ZuwpfjCwjPkAOxpjAEjabg6LRSfL7cAJb6gSQGZYjGhadlzKKywDkCUnJ+KEfrNY1jH5EEoSIKLCb572jSiglA==}
     cpu: [x64]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-x64-musl@4.26.0':
     resolution: {integrity: sha512-+HJD2lFS86qkeF8kNu0kALtifMpPCZU80HvwztIKnYwym3KnA1os6nsX4BGSTLtS2QVAGG1P3guRgsYyMA0Yhg==}
     cpu: [x64]
     os: [linux]
+    libc: [musl]
 
   '@rollup/rollup-win32-arm64-msvc@4.26.0':
     resolution: {integrity: sha512-WUQzVFWPSw2uJzX4j6YEbMAiLbs0BUysgysh8s817doAYhR5ybqTI1wtKARQKo6cGop3pHnrUJPFCsXdoFaimQ==}
@@ -4039,6 +4079,9 @@ packages:
     resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==}
     engines: {node: '>=12'}
 
+  ansi-sequence-parser@1.1.1:
+    resolution: {integrity: sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==}
+
   ansi-styles@1.1.0:
     resolution: {integrity: sha512-f2PKUkN5QngiSemowa6Mrk9MPCdtFiOSmibjZ+j1qhLGHHYsqZwmBMRF3IRMVXo8sybDqx2fJl2d/8OphBoWkA==}
     engines: {node: '>=0.10.0'}
@@ -4639,6 +4682,9 @@ packages:
     resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==}
     engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
 
+  codehike@1.0.4:
+    resolution: {integrity: sha512-mG/YJiK5J9tFHp/2seoliZpT4uxzjcbwDWXWXcYPRQKgQa4iRtwzVsOp/L4FnEX1J9LceZjCe3+ztSiktrcV1w==}
+
   collapse-white-space@2.1.0:
     resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==}
 
@@ -7309,24 +7355,28 @@ packages:
     engines: {node: '>= 12.0.0'}
     cpu: [arm64]
     os: [linux]
+    libc: [glibc]
 
   lightningcss-linux-arm64-musl@1.27.0:
     resolution: {integrity: sha512-rCGBm2ax7kQ9pBSeITfCW9XSVF69VX+fm5DIpvDZQl4NnQoMQyRwhZQm9pd59m8leZ1IesRqWk2v/DntMo26lg==}
     engines: {node: '>= 12.0.0'}
     cpu: [arm64]
     os: [linux]
+    libc: [musl]
 
   lightningcss-linux-x64-gnu@1.27.0:
     resolution: {integrity: sha512-Dk/jovSI7qqhJDiUibvaikNKI2x6kWPN79AQiD/E/KeQWMjdGe9kw51RAgoWFDi0coP4jinaH14Nrt/J8z3U4A==}
     engines: {node: '>= 12.0.0'}
     cpu: [x64]
     os: [linux]
+    libc: [glibc]
 
   lightningcss-linux-x64-musl@1.27.0:
     resolution: {integrity: sha512-QKjTxXm8A9s6v9Tg3Fk0gscCQA1t/HMoF7Woy1u68wCk5kS4fR+q3vXa1p3++REW784cRAtkYKrPy6JKibrEZA==}
     engines: {node: '>= 12.0.0'}
     cpu: [x64]
     os: [linux]
+    libc: [musl]
 
   lightningcss-win32-arm64-msvc@1.27.0:
     resolution: {integrity: sha512-/wXegPS1hnhkeG4OXQKEMQeJd48RDC3qdh+OA8pCuOPCyvnm/yEayrJdJVqzBsqpy1aJklRCVxscpFur80o6iQ==}
@@ -11965,6 +12015,10 @@ snapshots:
 
   '@chevrotain/utils@11.0.3': {}
 
+  '@code-hike/lighter@1.0.1':
+    dependencies:
+      ansi-sequence-parser: 1.1.1
+
   '@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.2)(@lezer/common@1.2.3)':
     dependencies:
       '@codemirror/language': 6.10.3
@@ -14971,6 +15025,8 @@ snapshots:
 
   ansi-regex@6.1.0: {}
 
+  ansi-sequence-parser@1.1.1: {}
+
   ansi-styles@1.1.0: {}
 
   ansi-styles@2.2.1: {}
@@ -15665,6 +15721,16 @@ snapshots:
 
   co@4.6.0: {}
 
+  codehike@1.0.4:
+    dependencies:
+      '@code-hike/lighter': 1.0.1
+      diff: 5.2.0
+      estree-util-visit: 2.0.0
+      mdast-util-mdx-jsx: 3.1.3
+      unist-util-visit: 5.0.0
+    transitivePeerDependencies:
+      - supports-color
+
   collapse-white-space@2.1.0: {}
 
   collect-v8-coverage@1.0.2: {}