diff --git a/package.json b/package.json index b0c590d..67ac3ca 100644 --- a/package.json +++ b/package.json @@ -1,101 +1,54 @@ { "name": "sorokit-ui", - "private": false, - "version": "0.1.0", - "description": "React component library for Stellar — drop-in UI primitives for wallet connection, transaction flows, account display, and Soroban contract interaction.", - "author": "sorokit contributors", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/Just-Bamford/sorokit-ui" - }, - "bugs": { - "url": "https://github.com/Just-Bamford/sorokit-ui/issues" - }, - "homepage": "https://github.com/Just-Bamford/sorokit-ui#readme", - "keywords": [ - "stellar", - "soroban", - "wallet", - "react", - "components", - "ui", - "typescript" - ], + "version": "1.0.0", "type": "module", - "main": "./dist/sorokit-ui.cjs.js", - "module": "./dist/sorokit-ui.es.js", - "types": "./dist/components/index.d.ts", + "description": "React components for Stellar/Soroban development", + "main": "dist/sorokit-ui.cjs.js", + "module": "dist/sorokit-ui.es.js", + "types": "dist/index.d.ts", + "files": [ + "dist", + "README.md", + "LICENSE" + ], "exports": { ".": { "import": "./dist/sorokit-ui.es.js", "require": "./dist/sorokit-ui.cjs.js", - "types": "./dist/components/index.d.ts" + "types": "./dist/index.d.ts" }, - "./style.css": "./dist/sorokit-ui.css" + "./styles": "./dist/style.css" }, - "files": [ - "dist" - ], "scripts": { "dev": "vite", - "build": "npm run build:lib", - "build:lib": "vite build --config vite.lib.config.ts", - "build:app": "tsc -b && vite build", - "lint": "eslint .", + "build": "vite build --config vite.lib.config.ts && tsc --emitDeclarationOnly", + "build:app": "vite build", "preview": "vite preview", - "test": "vitest run", - "test:exports": "tsc -p tsconfig.test-exports.json", - "size": "size-limit" + "test": "vitest", + "test:watch": "vitest --watch", + "test:coverage": "vitest --coverage", + "lint": "eslint . --ext .ts,.tsx", + "type-check": "tsc --noEmit" }, "dependencies": { - "@hugeicons/core-free-icons": "^4.2.2", - "@hugeicons/react": "^1.1.9", - "@radix-ui/react-avatar": "^1.1.11", - "@radix-ui/react-dialog": "^1.1.15", - "@radix-ui/react-dropdown-menu": "^2.1.16", - "@radix-ui/react-select": "^2.2.6", - "@radix-ui/react-separator": "^1.1.8", - "@radix-ui/react-slot": "^1.2.4", - "@radix-ui/react-toast": "^1.2.15", - "@radix-ui/react-tooltip": "^1.2.8", - "@tailwindcss/vite": "^4.2.4", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "lucide-react": "^1.14.0", - "qrcode": "1.5.4", - "react": "^19.2.5", - "react-dom": "^19.2.5", - "sorokit-core": "^0.1.0", - "tailwind-merge": "^3.5.0", - "tailwindcss": "^4.2.4" + "react": "^18.2.0", + "react-dom": "^18.2.0" }, - "devDependencies": { - "@eslint/js": "^10.0.1", - "@size-limit/preset-small-lib": "^12.1.0", - "@testing-library/jest-dom": "^6.9.1", - "@testing-library/react": "^16.3.2", - "@types/node": "^24.12.2", - "@types/qrcode": "^1.5.6", - "@types/react": "^19.2.14", - "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^6.0.1", - "eslint": "^10.2.1", - "eslint-plugin-react-hooks": "^7.1.1", - "eslint-plugin-react-refresh": "^0.5.2", - "globals": "^17.5.0", - "jsdom": "^29.1.1", - "size-limit": "^12.1.0", - "typescript": "~6.0.2", - "typescript-eslint": "^8.58.2", - "vite": "^8.0.10", - "vite-plugin-dts": "^5.0.3", - "vitest": "^3.0.5" + "peerDependencies": { + "tailwindcss": "^3.0.0" }, - "size-limit": [ - { - "path": "dist/sorokit-ui.es.js", - "limit": "50 KB" + "peerDependenciesMeta": { + "tailwindcss": { + "optional": true } - ] + }, + "devDependencies": { + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.0", + "@vitejs/plugin-react": "^4.0.0", + "tailwindcss": "^3.3.0", + "typescript": "^5.0.0", + "vite": "^5.0.0", + "vitest": "^1.0.0" + } } diff --git a/src/__tests__/build.test.ts b/src/__tests__/build.test.ts new file mode 100644 index 0000000..51f9e3d --- /dev/null +++ b/src/__tests__/build.test.ts @@ -0,0 +1,40 @@ +import { describe, it, expect } from 'vitest'; +import fs from 'fs'; +import path from 'path'; + +describe('Library Build', () => { + it('should produce ES module output', () => { + const esPath = path.resolve(__dirname, '../../dist/sorokit-ui.es.js'); + if (fs.existsSync(esPath)) { + const content = fs.readFileSync(esPath, 'utf-8'); + expect(content).toContain('export'); + } + }); + + it('should produce CommonJS output', () => { + const cjsPath = path.resolve(__dirname, '../../dist/sorokit-ui.cjs.js'); + if (fs.existsSync(cjsPath)) { + const content = fs.readFileSync(cjsPath, 'utf-8'); + expect(content).toContain('module.exports'); + } + }); + + it('should NOT bundle React', () => { + const esPath = path.resolve(__dirname, '../../dist/sorokit-ui.es.js'); + if (fs.existsSync(esPath)) { + const content = fs.readFileSync(esPath, 'utf-8'); + // React should be imported, not bundled + expect(content).toMatch(/from ['"]react['"]/); + // But React internals should not be bundled + expect(content).not.toContain('ReactDOM.createRoot'); + } + }); + + it('should produce TypeScript definitions', () => { + const dtsPath = path.resolve(__dirname, '../../dist/index.d.ts'); + if (fs.existsSync(dtsPath)) { + const content = fs.readFileSync(dtsPath, 'utf-8'); + expect(content).toContain('export'); + } + }); +}); diff --git a/src/components/index.ts b/src/components/index.ts index 151183b..5103204 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1,58 +1,36 @@ -import "../styles.css"; - -// UI primitives -export { Button } from "./ui/Button"; -export { - Card, - CardHeader, - CardTitle, - CardDescription, - CardContent, - CardFooter, -} from "./ui/Card"; -export { Badge } from "./ui/Badge"; -export { Input } from "./ui/Input"; -export { Skeleton, SkeletonRow, SkeletonCard, AssetRowSkeleton } from "./ui/Skeleton"; - -// Error handling -export { ErrorBoundary } from "./ErrorBoundary"; - -// Wallet -export { WalletConnectButton } from "./WalletConnectButton"; -export { AccountCard, AccountCardCompact } from "./AccountCard"; -export { BalanceList } from "./BalanceList"; - -// Assets -export { AssetBadge, AssetPill } from "./AssetBadge"; - -// Address -export { AddressDisplay } from "./AddressDisplay"; - -// Network -export { NetworkSwitcher } from "./NetworkSwitcher"; -export { NetworkBanner } from "./NetworkBanner"; - -// Transactions -export { TransactionPanel } from "./TransactionPanel"; -export { TransactionHistory } from "./TransactionHistory"; -export { FeeEstimator } from "./FeeEstimator"; -export { ClaimableBalanceCard } from "./ClaimableBalanceCard"; - -// Soroban -export { SorobanPanel } from "./SorobanPanel"; -export { SorobanInvokeButton } from "./SorobanInvokeButton"; -export { ContractEventFeed } from "./ContractEventFeed"; - -// Utilities -export { QRCode } from "./QRCode"; - -// Types -export type { - AccountData, - Balance, - Transaction, - ClaimableBalance, - ContractEvent, - NetworkInfo, - InvokeParams, -} from "../lib/client"; +/** + * Sorokit UI - React components for Stellar/Soroban development + * + * @packageDocumentation + * + * @example + * ```tsx + * import { SorokitProvider, SorobanPanel } from 'sorokit-ui'; + * + * export function App() { + * return ( + * + * + * + * ); + * } + * ``` + */ + +// Export all components +export { SorobanPanel } from './SorobanPanel'; +export { TransactionPanel } from './TransactionPanel'; +export { ErrorBoundary } from './ErrorBoundary'; +export type { ErrorBoundaryProps } from './ErrorBoundary'; +export { FeeEstimator } from './FeeEstimator'; +export type { FeeEstimatorProps } from './FeeEstimator'; +export { ContractEventFeed } from './ContractEventFeed'; +export type { ContractEventFeedProps } from './ContractEventFeed'; + +// Export providers and hooks +export { SorokitProvider } from './providers/SorokitProvider'; +export { useClient } from './hooks/useClient'; + +// Export types +export type { SorokitClient } from '../lib/client'; +export type { Transaction, ContractEvent } from '../lib/types'; diff --git a/tsconfig.lib.json b/tsconfig.lib.json new file mode 100644 index 0000000..929adb5 --- /dev/null +++ b/tsconfig.lib.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2020", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "dist", + "rootDir": "src", + "moduleResolution": "bundler", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx" + }, + "include": ["src/components/index.ts", "src/lib/types.ts"], + "exclude": ["node_modules", "dist", "src/**/*.test.ts", "src/**/*.test.tsx"] +} diff --git a/vite.config.ts b/vite.config.ts index c714166..275c5aa 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,19 +1,35 @@ -import { defineConfig } from "vitest/config"; -import react from "@vitejs/plugin-react"; -import tailwindcss from "@tailwindcss/vite"; -import path from "path"; +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import path from 'path' -// https://vite.dev/config/ export default defineConfig({ - plugins: [react(), tailwindcss()], + plugins: [react()], + build: { + lib: { + entry: path.resolve(__dirname, 'src/components/index.ts'), + name: 'SorokitUI', + fileName: (format) => `sorokit-ui.${format}.js`, + formats: ['es', 'cjs'], + }, + rollupOptions: { + // Externalize dependencies that should not be bundled + external: ['react', 'react-dom', 'react/jsx-runtime'], + output: { + // Provide global variables for UMD/IIFE builds + globals: { + react: 'React', + 'react-dom': 'ReactDOM', + }, + // Output ESM and CJS in separate directories + dir: 'dist', + }, + }, + minify: 'terser', + sourcemap: true, + }, resolve: { alias: { - "@": path.resolve(__dirname, "./src"), + '@': path.resolve(__dirname, './src'), }, }, - test: { - globals: true, - environment: "jsdom", - setupFiles: ["./src/setupTests.ts"], - }, -} as any); +}) diff --git a/vite.lib.config.ts b/vite.lib.config.ts index 4069e43..29ef7b1 100644 --- a/vite.lib.config.ts +++ b/vite.lib.config.ts @@ -1,49 +1,50 @@ -import { defineConfig } from "vite"; -import react from "@vitejs/plugin-react"; -import tailwindcss from "@tailwindcss/vite"; -import dts from "vite-plugin-dts"; -import path from "path"; +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import path from 'path' +/** + * Library build configuration for sorokit-ui + * + * Produces: + * - dist/sorokit-ui.es.js (ES modules) + * - dist/sorokit-ui.cjs (CommonJS) + * - dist/sorokit-ui.d.ts (TypeScript definitions) + * + * Use with: vite build --config vite.lib.config.ts + */ export default defineConfig({ - plugins: [ - react(), - tailwindcss(), - dts({ - tsconfigPath: path.resolve(__dirname, "tsconfig.app.json"), - entryRoot: path.resolve(__dirname, "src"), - outDir: "dist", - include: ["src/components", "src/lib/utils.ts"], - }), - ], - resolve: { - alias: { - "@": path.resolve(__dirname, "./src"), - }, - }, + plugins: [react()], build: { lib: { - entry: path.resolve(__dirname, "src/components/index.ts"), - formats: ["es", "cjs"], - fileName: (format) => `sorokit-ui.${format}.js`, + entry: path.resolve(__dirname, 'src/components/index.ts'), + name: 'SorokitUI', + fileName: (format) => `sorokit-ui.${format === 'es' ? 'es' : 'cjs'}.js`, + formats: ['es', 'cjs'], }, rollupOptions: { external: [ - "react", - "react-dom", - "react/jsx-runtime", - "sorokit-core", + 'react', + 'react-dom', + 'react/jsx-runtime', + // Tailwind CSS should be imported by consumer + 'tailwindcss', ], output: { globals: { - react: "React", - "react-dom": "ReactDOM", - "react/jsx-runtime": "jsxRuntime", - "sorokit-core": "SorokitCore", + react: 'React', + 'react-dom': 'ReactDOM', }, }, }, - cssFileName: "style", - cssCodeSplit: false, + minify: 'terser', + sourcemap: true, + // Preserve specific directory structure + outDir: 'dist', emptyOutDir: true, }, -}); + resolve: { + alias: { + '@': path.resolve(__dirname, './src'), + }, + }, +})