diff --git a/README.md b/README.md index d880f97..31d99d9 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,10 @@ This Turborepo includes the following example applications: | `@shelby-protocol/ai-image-generation` | Shelby AI image generation example | [`apps/ai-image-generation`](./apps/ai-image-generation) | | `@shelby-protocol/cross-chain-accounts` | Shelby cross chain accounts example | [`apps/cross-chain-accounts`](./apps/cross-chain-accounts) | | `@shelby-protocol/download-example` | An example app to demonstrate downloading blobs using the Shelby SDK | [`apps/download-blob`](./apps/download-blob) | +| `@shelby-examples/ethereum-file-upload` | Simple Ethereum file upload to Shelby using wagmi and RainbowKit | [`apps/ethereum/file-upload`](./apps/ethereum/file-upload) | | `@shelby-protocol/list-example` | An example app to demonstrate listing blobs using the Shelby SDK | [`apps/list-blob`](./apps/list-blob) | +| `solana-file-upload` | Simple Solana file upload to Shelby using @solana/react-hooks | [`apps/solana/file-upload`](./apps/solana/file-upload) | +| `token-gated` | Next.js, Tailwind, @solana/react-hooks, Anchor vault program | [`apps/solana/token-gated`](./apps/solana/token-gated) | | `@shelby-protocol/upload-example` | An example app to demonstrate uploading blobs using the Shelby SDK | [`apps/upload-blob`](./apps/upload-blob) | diff --git a/apps/ethereum/file-upload/package.json b/apps/ethereum/file-upload/package.json index 0128a07..8330b25 100644 --- a/apps/ethereum/file-upload/package.json +++ b/apps/ethereum/file-upload/package.json @@ -1,5 +1,6 @@ { "name": "@shelby-examples/ethereum-file-upload", + "description": "Simple Ethereum file upload to Shelby using wagmi and RainbowKit", "private": true, "version": "0.0.0", "scripts": { diff --git a/packages/table-generator/src/scanner.ts b/packages/table-generator/src/scanner.ts index 92925a5..746abbd 100644 --- a/packages/table-generator/src/scanner.ts +++ b/packages/table-generator/src/scanner.ts @@ -1,7 +1,9 @@ -import { readdirSync, readFileSync, statSync } from "node:fs"; +import { existsSync, readdirSync, readFileSync, statSync } from "node:fs"; import { join } from "node:path"; import type { MonorepoApps, MonorepoItem, PackageInfo } from "./types.js"; +const IGNORED_DIRS = new Set(["node_modules"]); + /** * Get package.json content for a given path */ @@ -23,6 +25,11 @@ function getPackageInfo(packagePath: string): PackageInfo | null { /** * Scan a directory for packages/apps + * + * Workspaces may be nested one or more levels deep (for example + * `apps/solana/file-upload`, declared in pnpm-workspace.yaml as + * `apps/solana/*`), so directories without a package.json of their own are + * treated as grouping directories and recursed into. */ function scanDirectory( dirPath: string, @@ -32,37 +39,52 @@ function scanDirectory( const items: MonorepoItem[] = []; try { - const dirs = readdirSync(dirPath); + // Sorted so the generated table is stable across filesystems + const dirs = readdirSync(dirPath).sort(); for (const dir of dirs) { + if (dir.startsWith(".") || IGNORED_DIRS.has(dir)) { + continue; + } + const fullPath = join(dirPath, dir); - if (statSync(fullPath).isDirectory()) { - const pkgInfo = getPackageInfo(fullPath); - if (pkgInfo) { - let repository: string | undefined; - if (typeof pkgInfo.repository === "string") { - repository = pkgInfo.repository; - } else if ( - typeof pkgInfo.repository === "object" && - pkgInfo.repository?.url - ) { - repository = pkgInfo.repository.url; - } + if (!statSync(fullPath).isDirectory()) { + continue; + } - if (repository) { - // Clean up git URLs - repository = repository.replace(/^git\+/, "").replace(/\.git$/, ""); - } + const itemPath = `${relativePath}/${dir}`; - items.push({ - name: pkgInfo.name, - description: pkgInfo.description || "No description provided", - path: `${relativePath}/${dir}`, - type, - private: pkgInfo.private || false, - homepage: pkgInfo.homepage, - repository, - }); + // Grouping directory: no package.json of its own, so look inside it + if (!existsSync(join(fullPath, "package.json"))) { + items.push(...scanDirectory(fullPath, type, itemPath)); + continue; + } + + const pkgInfo = getPackageInfo(fullPath); + if (pkgInfo) { + let repository: string | undefined; + if (typeof pkgInfo.repository === "string") { + repository = pkgInfo.repository; + } else if ( + typeof pkgInfo.repository === "object" && + pkgInfo.repository?.url + ) { + repository = pkgInfo.repository.url; + } + + if (repository) { + // Clean up git URLs + repository = repository.replace(/^git\+/, "").replace(/\.git$/, ""); } + + items.push({ + name: pkgInfo.name, + description: pkgInfo.description || "No description provided", + path: itemPath, + type, + private: pkgInfo.private || false, + homepage: pkgInfo.homepage, + repository, + }); } } } catch (error) {