@vx-oss/docs-shadcn 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Fuma
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,11 @@
1
+ import { GetManualInstallationOptions, ManualInstallationSnippet } from "./manual-installation.js";
2
+ //#region src/index.d.ts
3
+ interface ShadcnRegistryOptions {
4
+ /** file path of `registry.json` */
5
+ registryPath: string;
6
+ }
7
+ declare function createShadcnDocs(options: ShadcnRegistryOptions): {
8
+ getManualInstallation: (options: GetManualInstallationOptions) => Promise<ManualInstallationSnippet[]>;
9
+ };
10
+ //#endregion
11
+ export { type GetManualInstallationOptions, type ManualInstallationSnippet, ShadcnRegistryOptions, createShadcnDocs };
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ import { getManualInstallation } from "./manual-installation.js";
2
+ import path from "node:path";
3
+ //#region src/index.ts
4
+ function createShadcnDocs(options) {
5
+ const ctx = {
6
+ registryJsonPath: options.registryPath,
7
+ dir: path.dirname(options.registryPath)
8
+ };
9
+ return { getManualInstallation: getManualInstallation.bind(ctx) };
10
+ }
11
+ //#endregion
12
+ export { createShadcnDocs };
@@ -0,0 +1,36 @@
1
+ import "./types.js";
2
+ import { PM } from "./utils.js";
3
+ //#region src/manual-installation.d.ts
4
+ type ManualInstallationSnippet = {
5
+ lang: string;
6
+ code: string;
7
+ path: string;
8
+ title: string;
9
+ kind: 'file';
10
+ } | {
11
+ kind: 'docs';
12
+ content: string;
13
+ } | {
14
+ kind: 'cssVars' | 'css' | 'tailwind' | 'envVars';
15
+ lang: string;
16
+ code: string;
17
+ } | {
18
+ kind: 'dependencies' | 'devDependencies' | 'registryDependencies';
19
+ lang: string;
20
+ codeTabs: Record<PM, string>;
21
+ dependencies: string[];
22
+ };
23
+ interface GetManualInstallationOptions {
24
+ /**
25
+ * Registry item name to generate snippets for.
26
+ */
27
+ name: string;
28
+ /**
29
+ * Include snippets from `registryDependencies` that exist in the same registry.
30
+ *
31
+ * @defaultValue `true`
32
+ */
33
+ includeRegistryDependencies?: boolean;
34
+ }
35
+ //#endregion
36
+ export { GetManualInstallationOptions, ManualInstallationSnippet };
@@ -0,0 +1,187 @@
1
+ import { formatAddCommand, formatInstallCommand, getRegistryItemPath, resolveRegistryDependency } from "./utils.js";
2
+ import path from "node:path";
3
+ import fs from "node:fs/promises";
4
+ //#region src/manual-installation.ts
5
+ async function getManualInstallation(options) {
6
+ const { name, includeRegistryDependencies = true } = options;
7
+ const items = await collectItems.call(this, name, includeRegistryDependencies);
8
+ if (items.length === 0) return [];
9
+ const snippets = [];
10
+ const dependencies = /* @__PURE__ */ new Set();
11
+ const devDependencies = /* @__PURE__ */ new Set();
12
+ const registryDependencies = /* @__PURE__ */ new Set();
13
+ for (const item of items) {
14
+ if (item.dependencies) for (const dep of item.dependencies) dependencies.add(dep);
15
+ if (item.devDependencies) for (const dep of item.devDependencies) devDependencies.add(dep);
16
+ if (!includeRegistryDependencies && item.registryDependencies) for (const dep of item.registryDependencies) registryDependencies.add(dep);
17
+ if (item.docs) snippets.push({
18
+ kind: "docs",
19
+ content: item.docs
20
+ });
21
+ if (item.envVars && Object.keys(item.envVars).length > 0) snippets.push({
22
+ kind: "envVars",
23
+ lang: "dotenv",
24
+ code: formatEnvVars(item.envVars)
25
+ });
26
+ if (item.cssVars && hasCssVars(item.cssVars)) snippets.push({
27
+ kind: "cssVars",
28
+ lang: "css",
29
+ code: formatCssVars(item.cssVars)
30
+ });
31
+ if (item.css && Object.keys(item.css).length > 0) snippets.push({
32
+ kind: "css",
33
+ lang: "css",
34
+ code: formatCssRules(item.css)
35
+ });
36
+ if (item.tailwind?.config && Object.keys(item.tailwind.config).length > 0) snippets.push({
37
+ kind: "tailwind",
38
+ lang: "ts",
39
+ code: `export default ${JSON.stringify(item.tailwind.config, null, 2)}`
40
+ });
41
+ for (const file of item.files ?? []) {
42
+ if (!file.content) continue;
43
+ const displayPath = getDisplayPath(file);
44
+ snippets.push({
45
+ kind: "file",
46
+ title: path.basename(file.path),
47
+ lang: getLanguage(file.path),
48
+ code: file.content,
49
+ path: displayPath
50
+ });
51
+ }
52
+ }
53
+ return [...createInstallSnippets({
54
+ dependencies: Array.from(dependencies),
55
+ devDependencies: Array.from(devDependencies),
56
+ registryDependencies: Array.from(registryDependencies)
57
+ }), ...snippets];
58
+ }
59
+ async function collectItems(name, includeRegistryDependencies) {
60
+ const all = /* @__PURE__ */ new Map();
61
+ const queue = [getRegistryItemPath.call(this, name)];
62
+ for (const item of queue) {
63
+ const content = await fs.readFile(item, "utf-8").catch(() => null);
64
+ if (!content) {
65
+ all.set(item, null);
66
+ continue;
67
+ }
68
+ const parsed = JSON.parse(content);
69
+ all.set(item, parsed);
70
+ if (!includeRegistryDependencies || !parsed.registryDependencies) continue;
71
+ for (const dep of parsed.registryDependencies) {
72
+ const { local } = resolveRegistryDependency(dep);
73
+ if (!local) continue;
74
+ const depPath = getRegistryItemPath.call(this, dep);
75
+ if (!all.has(depPath)) queue.push(depPath);
76
+ }
77
+ }
78
+ const out = [];
79
+ for (const v of all.values()) if (v) out.push(v);
80
+ return out;
81
+ }
82
+ function createInstallSnippets({ dependencies, devDependencies, registryDependencies }) {
83
+ const snippets = [];
84
+ if (dependencies.length > 0) snippets.push({
85
+ kind: "dependencies",
86
+ lang: "bash",
87
+ codeTabs: formatInstallCommand(dependencies),
88
+ dependencies
89
+ });
90
+ if (devDependencies.length > 0) snippets.push({
91
+ kind: "devDependencies",
92
+ lang: "bash",
93
+ codeTabs: formatInstallCommand(devDependencies, true),
94
+ dependencies: devDependencies
95
+ });
96
+ if (registryDependencies.length > 0) snippets.push({
97
+ kind: "registryDependencies",
98
+ lang: "bash",
99
+ codeTabs: formatAddCommand(registryDependencies),
100
+ dependencies: registryDependencies
101
+ });
102
+ return snippets;
103
+ }
104
+ function getDisplayPath(file) {
105
+ if (file.target) return file.target.startsWith("~/") ? file.target.slice(2) : file.target;
106
+ switch (file.type) {
107
+ case "registry:ui": return `components/ui/${path.basename(file.path)}`;
108
+ case "registry:component": return `components/${path.basename(file.path)}`;
109
+ case "registry:hook": return `lib/hooks/${path.basename(file.path)}`;
110
+ case "registry:lib": return `lib/${path.basename(file.path)}`;
111
+ case "registry:block": return `components/${path.basename(file.path)}`;
112
+ default: return file.path;
113
+ }
114
+ }
115
+ /** get Shiki supported language/grammar */
116
+ function getLanguage(filePath) {
117
+ if (path.basename(filePath).startsWith(".env")) return "dotenv";
118
+ const ext = path.extname(filePath);
119
+ switch (ext) {
120
+ case ".mts":
121
+ case ".cts": return "ts";
122
+ case ".mjs":
123
+ case ".cjs": return "js";
124
+ default: return ext.slice(1);
125
+ }
126
+ }
127
+ function hasCssVars(cssVars) {
128
+ return Boolean(cssVars.theme && Object.keys(cssVars.theme).length > 0 || cssVars.light && Object.keys(cssVars.light).length > 0 || cssVars.dark && Object.keys(cssVars.dark).length > 0);
129
+ }
130
+ function formatCssVars(cssVars) {
131
+ const lines = ["@layer base {"];
132
+ const rootVars = {
133
+ ...cssVars.theme,
134
+ ...cssVars.light
135
+ };
136
+ if (Object.keys(rootVars).length > 0) {
137
+ lines.push(" :root {");
138
+ for (const [key, value] of Object.entries(rootVars)) {
139
+ lines.push(` /* [!code ++] */`);
140
+ lines.push(` --${key}: ${value};`);
141
+ }
142
+ lines.push(" }");
143
+ }
144
+ if (cssVars.dark && Object.keys(cssVars.dark).length > 0) {
145
+ lines.push(" .dark {");
146
+ for (const [key, value] of Object.entries(cssVars.dark)) {
147
+ lines.push(` /* [!code ++] */`);
148
+ lines.push(` --${key}: ${value};`);
149
+ }
150
+ lines.push(" }");
151
+ }
152
+ lines.push("}");
153
+ return lines.join("\n");
154
+ }
155
+ function formatCssRules(css, indent = 0) {
156
+ const pad = " ".repeat(indent);
157
+ const lines = [];
158
+ for (const [selector, value] of Object.entries(css)) {
159
+ if (typeof value === "string") {
160
+ lines.push(`${pad}/* [!code ++] */`);
161
+ lines.push(`${pad}${selector}: ${value};`);
162
+ continue;
163
+ }
164
+ if (Array.isArray(value)) {
165
+ lines.push(`${pad}/* [!code ++] */`);
166
+ lines.push(`${pad}${selector} ${value.join(" ")};`);
167
+ continue;
168
+ }
169
+ if (value && typeof value === "object") {
170
+ if (selector.startsWith("@")) {
171
+ lines.push(`${pad}${selector} {`);
172
+ lines.push(formatCssRules(value, indent + 1));
173
+ lines.push(`${pad}}`);
174
+ } else {
175
+ lines.push(`${pad}${selector} {`);
176
+ lines.push(formatCssRules(value, indent + 1));
177
+ lines.push(`${pad}}`);
178
+ }
179
+ }
180
+ }
181
+ return lines.join("\n");
182
+ }
183
+ function formatEnvVars(envVars) {
184
+ return Object.entries(envVars).map(([key, value]) => `${key}=${JSON.stringify(value)}`).join("\n");
185
+ }
186
+ //#endregion
187
+ export { getManualInstallation };
package/dist/rsc.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { ManualInstallationSnippet } from "./manual-installation.js";
2
+ //#region src/rsc.d.ts
3
+ declare function Snippet({ item }: {
4
+ item: ManualInstallationSnippet;
5
+ }): Promise<import("react").JSX.Element>;
6
+ //#endregion
7
+ export { Snippet };
package/dist/rsc.js ADDED
@@ -0,0 +1,50 @@
1
+ import { ServerCodeBlock } from "@vx-oss/docs-react/components/codeblock.rsc";
2
+ import { transformerIcon } from "@vx-oss/docs-core/mdx-plugins/transformer-icon";
3
+ import { CodeBlockTab, CodeBlockTabs, CodeBlockTabsList, CodeBlockTabsTrigger } from "@vx-oss/docs-react/components/codeblock";
4
+ import { transformerNotationDiff } from "@shikijs/transformers";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ //#region src/rsc.tsx
7
+ const transformers = [transformerNotationDiff(), transformerIcon()];
8
+ function getTitle(item) {
9
+ switch (item.kind) {
10
+ case "file": return item.path;
11
+ case "cssVars":
12
+ case "css": return "globals.css";
13
+ case "envVars": return ".env.local";
14
+ case "tailwind": return "tailwind.config.js";
15
+ }
16
+ }
17
+ async function Snippet({ item }) {
18
+ if (item.kind === "docs") return /* @__PURE__ */ jsx("p", {
19
+ className: "text-fd-muted-foreground text-sm whitespace-pre-wrap",
20
+ children: item.content
21
+ });
22
+ switch (item.kind) {
23
+ case "dependencies":
24
+ case "devDependencies":
25
+ case "registryDependencies":
26
+ const tabs = Object.entries(item.codeTabs);
27
+ return /* @__PURE__ */ jsxs(CodeBlockTabs, {
28
+ defaultValue: tabs[0][0],
29
+ children: [/* @__PURE__ */ jsx(CodeBlockTabsList, { children: tabs.map(([t]) => /* @__PURE__ */ jsx(CodeBlockTabsTrigger, {
30
+ value: t,
31
+ children: t
32
+ }, t)) }), tabs.map(([k, v]) => /* @__PURE__ */ jsx(CodeBlockTab, {
33
+ value: k,
34
+ children: /* @__PURE__ */ jsx(ServerCodeBlock, {
35
+ code: v,
36
+ lang: item.lang
37
+ })
38
+ }, k))]
39
+ });
40
+ }
41
+ const title = getTitle(item);
42
+ return /* @__PURE__ */ jsx(ServerCodeBlock, {
43
+ code: item.code,
44
+ lang: item.lang,
45
+ codeblock: { title },
46
+ transformers
47
+ });
48
+ }
49
+ //#endregion
50
+ export { Snippet };
@@ -0,0 +1,2 @@
1
+ import "shadcn/schema";
2
+ import "zod-3";
@@ -0,0 +1,5 @@
1
+ import "./types.js";
2
+ //#region src/utils.d.ts
3
+ type PM = 'npm' | 'pnpm' | 'yarn' | 'bun';
4
+ //#endregion
5
+ export { PM };
package/dist/utils.js ADDED
@@ -0,0 +1,28 @@
1
+ import path from "node:path";
2
+ //#region src/utils.ts
3
+ function getRegistryItemPath(name) {
4
+ return path.join(this.dir, name + ".json");
5
+ }
6
+ function formatAddCommand(components) {
7
+ const list = components.join(" ");
8
+ return {
9
+ npm: `npx shadcn@latest add ${list}`,
10
+ pnpm: `pnpm dlx shadcn@latest add ${list}`,
11
+ yarn: `yarn dlx shadcn@latest add ${list}`,
12
+ bun: `bunx shadcn@latest add ${list}`
13
+ };
14
+ }
15
+ function formatInstallCommand(packages, dev = false) {
16
+ const pkgList = packages.join(" ");
17
+ return {
18
+ npm: dev ? `npm install -D ${pkgList}` : `npm install ${pkgList}`,
19
+ pnpm: dev ? `pnpm add -D ${pkgList}` : `pnpm add ${pkgList}`,
20
+ yarn: dev ? `yarn add -D ${pkgList}` : `yarn add ${pkgList}`,
21
+ bun: dev ? `bun add -d ${pkgList}` : `bun add ${pkgList}`
22
+ };
23
+ }
24
+ function resolveRegistryDependency(dep) {
25
+ return { local: !dep.startsWith("http://") && !dep.startsWith("https://") };
26
+ }
27
+ //#endregion
28
+ export { formatAddCommand, formatInstallCommand, getRegistryItemPath, resolveRegistryDependency };
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@vx-oss/docs-shadcn",
3
+ "version": "1.0.3",
4
+ "description": "Shadcn UI integration for Fumadocs.",
5
+ "keywords": [
6
+ "Fumadocs"
7
+ ],
8
+ "license": "MIT",
9
+ "author": "Fuma Nama",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/vezham/fumadocs",
13
+ "directory": "packages/shadcn"
14
+ },
15
+ "files": [
16
+ "css",
17
+ "dist"
18
+ ],
19
+ "type": "module",
20
+ "types": "./dist/index.d.ts",
21
+ "exports": {
22
+ ".": "./dist/index.js",
23
+ "./rsc": "./dist/rsc.js",
24
+ "./package.json": "./package.json"
25
+ },
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "dependencies": {
30
+ "@shikijs/transformers": "^4.4.3",
31
+ "zod-3": "npm:zod@^3.25.76"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "26.3.0",
35
+ "@types/react": "^19.2.18",
36
+ "react": "^19.2.8",
37
+ "react-dom": "^19.2.8",
38
+ "shadcn": "^4.19.0",
39
+ "tailwindcss": "^4.3.3",
40
+ "tsdown": "0.22.14",
41
+ "@vx-oss/docs-core": "1.0.3",
42
+ "@vx-oss/docs-react": "1.0.3",
43
+ "tsconfig": "0.0.1"
44
+ },
45
+ "peerDependencies": {
46
+ "@types/react": "*",
47
+ "react": "^19.2.0",
48
+ "react-dom": "^19.2.0",
49
+ "shadcn": "^4",
50
+ "@vx-oss/docs-core": "^1.0.1",
51
+ "@vx-oss/docs-react": "^1.0.1"
52
+ },
53
+ "peerDependenciesMeta": {
54
+ "shadcn": {
55
+ "optional": true
56
+ },
57
+ "@types/react": {
58
+ "optional": true
59
+ }
60
+ },
61
+ "scripts": {
62
+ "build": "tsdown",
63
+ "clean": "rimraf dist",
64
+ "dev": "tsdown --watch",
65
+ "lint": "oxlint .",
66
+ "types:check": "tsc --noEmit"
67
+ }
68
+ }