From d3b6eff929ab347dba2f1fa2cb7447d8459ab96f Mon Sep 17 00:00:00 2001 From: shadcn Date: Sat, 14 Dec 2024 19:13:06 +0400 Subject: [PATCH] feat(www): code blocks package manager (#6075) * feat(www): code blocks * fix: code style --- components/code-block-command.tsx | 108 ++++++++++++++++++++++++++++ components/mdx-components.tsx | 33 ++++----- content/docs/installation/remix.mdx | 2 +- content/docs/installation/vite.mdx | 7 +- hooks/use-config.ts | 2 + lib/rehype-component.ts | 13 ++-- lib/rehype-npm-command.ts | 20 +++++- 7 files changed, 159 insertions(+), 26 deletions(-) create mode 100644 components/code-block-command.tsx diff --git a/components/code-block-command.tsx b/components/code-block-command.tsx new file mode 100644 index 00000000000..f74e21f1693 --- /dev/null +++ b/components/code-block-command.tsx @@ -0,0 +1,108 @@ +"use client" + +import * as React from "react" +import { CheckIcon, ClipboardIcon } from "lucide-react" + +import { NpmCommands } from "@/types/unist" +import { useConfig } from "@/hooks/use-config" +import { copyToClipboardWithMeta } from "@/components/copy-button" +import { Tabs } from "@/registry/default/ui/tabs" +import { Button } from "@/registry/new-york/ui/button" +import { TabsContent, TabsList, TabsTrigger } from "@/registry/new-york/ui/tabs" + +export function CodeBlockCommand({ + __npmCommand__, + __yarnCommand__, + __pnpmCommand__, + __bunCommand__, +}: React.ComponentProps<"pre"> & NpmCommands) { + const [config, setConfig] = useConfig() + const [hasCopied, setHasCopied] = React.useState(false) + + React.useEffect(() => { + if (hasCopied) { + const timer = setTimeout(() => setHasCopied(false), 2000) + return () => clearTimeout(timer) + } + }, [hasCopied]) + + const packageManager = config.packageManager || "pnpm" + const tabs = React.useMemo(() => { + return { + pnpm: __pnpmCommand__, + npm: __npmCommand__, + yarn: __yarnCommand__, + bun: __bunCommand__, + } + }, [__npmCommand__, __pnpmCommand__, __yarnCommand__, __bunCommand__]) + + const copyCommand = React.useCallback(() => { + const command = tabs[packageManager] + + if (!command) { + return + } + + copyToClipboardWithMeta(command, { + name: "copy_npm_command", + properties: { + command, + pm: packageManager, + }, + }) + setHasCopied(true) + }, [packageManager, tabs]) + + return ( +
+ { + setConfig({ + ...config, + packageManager: value as "pnpm" | "npm" | "yarn" | "bun", + }) + }} + > +
+ + {Object.entries(tabs).map(([key, value]) => { + return ( + + {key} + + ) + })} + +
+ {Object.entries(tabs).map(([key, value]) => { + return ( + +
+                
+                  {value}
+                
+              
+
+ ) + })} +
+ +
+ ) +} diff --git a/components/mdx-components.tsx b/components/mdx-components.tsx index 73fc1b39264..680ae3ce7f9 100644 --- a/components/mdx-components.tsx +++ b/components/mdx-components.tsx @@ -11,6 +11,7 @@ import { Event } from "@/lib/events" import { cn } from "@/lib/utils" import { useConfig } from "@/hooks/use-config" import { Callout } from "@/components/callout" +import { CodeBlockCommand } from "@/components/code-block-command" import { CodeBlockWrapper } from "@/components/code-block-wrapper" import { ComponentExample } from "@/components/component-example" import { ComponentPreview } from "@/components/component-preview" @@ -192,16 +193,30 @@ const components = { __src__?: string __event__?: Event["name"] } & NpmCommands) => { + const isNpmCommand = + __npmCommand__ && __yarnCommand__ && __pnpmCommand__ && __bunCommand__ + + if (isNpmCommand) { + return ( + + ) + } + return (
-        {__rawString__ && !__npmCommand__ && (
+        {__rawString__ && (
           
         )}
-        {__npmCommand__ &&
-          __yarnCommand__ &&
-          __pnpmCommand__ &&
-          __bunCommand__ && (
-            
-          )}
       
     )
   },
diff --git a/content/docs/installation/remix.mdx b/content/docs/installation/remix.mdx
index 170d20a8a44..a579e8213c5 100644
--- a/content/docs/installation/remix.mdx
+++ b/content/docs/installation/remix.mdx
@@ -47,7 +47,7 @@ Do you want to use CSS variables for colors? › no / yes
 ### Install Tailwind CSS
 
 ```bash
-npm add -D tailwindcss@latest autoprefixer@latest
+npm install -D tailwindcss@latest autoprefixer@latest
 ```
 
 Then we create a `postcss.config.js` file:
diff --git a/content/docs/installation/vite.mdx b/content/docs/installation/vite.mdx
index 2b74834a857..a286dbf468f 100644
--- a/content/docs/installation/vite.mdx
+++ b/content/docs/installation/vite.mdx
@@ -19,7 +19,9 @@ Install `tailwindcss` and its peer dependencies, then generate your `tailwind.co
 
 ```bash
 npm install -D tailwindcss postcss autoprefixer
+```
 
+```bash
 npx tailwindcss init -p
 ```
 
@@ -93,11 +95,10 @@ Add the following code to the `tsconfig.app.json` file to resolve paths, for you
 
 ### Update vite.config.ts
 
-Add the following code to the vite.config.ts so your app can resolve paths without error
+Add the following code to the vite.config.ts so your app can resolve paths without error:
 
 ```bash
-# (so you can import "path" without error)
-npm i -D @types/node
+npm install -D @types/node
 ```
 
 ```typescript
diff --git a/hooks/use-config.ts b/hooks/use-config.ts
index 8f0b26b2feb..982e9ba0e06 100644
--- a/hooks/use-config.ts
+++ b/hooks/use-config.ts
@@ -8,12 +8,14 @@ type Config = {
   style: Style["name"]
   theme: BaseColor["name"]
   radius: number
+  packageManager: "npm" | "yarn" | "pnpm" | "bun"
 }
 
 const configAtom = atomWithStorage("config", {
   style: "new-york",
   theme: "zinc",
   radius: 0.5,
+  packageManager: "pnpm",
 })
 
 export function useConfig() {
diff --git a/lib/rehype-component.ts b/lib/rehype-component.ts
index eb1e863a8f8..c43aa8d8721 100644
--- a/lib/rehype-component.ts
+++ b/lib/rehype-component.ts
@@ -37,11 +37,14 @@ export function rehypeComponent() {
             } else {
               const component = Index[style.name][name]
               src = fileName
-                ? component.files.find((file: string) => {
-                    return (
-                      file.endsWith(`${fileName}.tsx`) ||
-                      file.endsWith(`${fileName}.ts`)
-                    )
+                ? component.files.find((file: unknown) => {
+                    if (typeof file === "string") {
+                      return (
+                        file.endsWith(`${fileName}.tsx`) ||
+                        file.endsWith(`${fileName}.ts`)
+                      )
+                    }
+                    return false
                   }) || component.files[0]?.path
                 : component.files[0]?.path
             }
diff --git a/lib/rehype-npm-command.ts b/lib/rehype-npm-command.ts
index 68157b66e4c..e0dd764da00 100644
--- a/lib/rehype-npm-command.ts
+++ b/lib/rehype-npm-command.ts
@@ -26,7 +26,7 @@ export function rehypeNpmCommand() {
         )
       }
 
-      // npx create.
+      // npx create-.
       if (node.properties?.["__rawString__"]?.startsWith("npx create-")) {
         const npmCommand = node.properties?.["__rawString__"]
         node.properties["__npmCommand__"] = npmCommand
@@ -44,6 +44,24 @@ export function rehypeNpmCommand() {
         )
       }
 
+      // npm create.
+      if (node.properties?.["__rawString__"]?.startsWith("npm create")) {
+        const npmCommand = node.properties?.["__rawString__"]
+        node.properties["__npmCommand__"] = npmCommand
+        node.properties["__yarnCommand__"] = npmCommand.replace(
+          "npm create",
+          "yarn create"
+        )
+        node.properties["__pnpmCommand__"] = npmCommand.replace(
+          "npm create",
+          "pnpm create"
+        )
+        node.properties["__bunCommand__"] = npmCommand.replace(
+          "npm create",
+          "bun create"
+        )
+      }
+
       // npx.
       if (
         node.properties?.["__rawString__"]?.startsWith("npx") &&