Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
check:
name: lint + typecheck + test
runs-on: ubuntu-latest
timeout-minutes: 10
timeout-minutes: 15
permissions:
contents: read
steps:
Expand All @@ -31,6 +31,7 @@ jobs:
- run: pnpm build
- run: pnpm typecheck
- run: pnpm test
- run: pnpm smoke:consumer

commitlint:
name: commitlint
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
release:
name: Release
runs-on: ubuntu-latest
timeout-minutes: 20
timeout-minutes: 25
if: github.repository == 'sumfxn/usdh-kit' && vars.RELEASES_ENABLED == 'true'
permissions:
contents: write
Expand All @@ -35,6 +35,7 @@ jobs:
- run: pnpm build
- run: pnpm typecheck
- run: pnpm test
- run: pnpm smoke:consumer
- name: Verify npm publish credentials
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Expand Down
90 changes: 52 additions & 38 deletions apps/demo/src/components/ConnectButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,63 @@

import { ConnectKitButton } from 'connectkit'

type ConnectKitRenderProps = {
isConnected: boolean
isConnecting: boolean
show?: () => void
address?: `0x${string}`
ensName?: string
}

export function ConnectButton() {
return <ConnectKitButton.Custom>{renderConnectButton}</ConnectKitButton.Custom>
}

function renderConnectButton({
isConnected,
isConnecting,
show,
address,
ensName,
}: ConnectKitRenderProps) {
const openConnectKit = () => show?.()

if (isConnecting) {
return (
<span className="inline-flex h-8 items-center rounded-md border border-neutral-200 bg-neutral-50 px-3 text-[11px] text-neutral-500 dark:border-neutral-800 dark:bg-neutral-900/60 dark:text-neutral-400">
Connecting...
</span>
)
}
if (!isConnected) {
return (
<button
type="button"
onClick={openConnectKit}
className="inline-flex h-8 items-center rounded-md bg-neutral-900 px-3.5 text-[11px] font-medium text-neutral-100 transition hover:bg-black dark:bg-neutral-100 dark:text-neutral-900 dark:hover:bg-white"
>
Connect wallet
</button>
)
}
const label = ensName ?? truncate(address)
return (
<ConnectKitButton.Custom>
{({ isConnected, isConnecting, show, address, ensName }) => {
if (isConnecting) {
return (
<span className="inline-flex h-8 items-center rounded-md border border-neutral-200 bg-neutral-50 px-3 text-[11px] text-neutral-500 dark:border-neutral-800 dark:bg-neutral-900/60 dark:text-neutral-400">
Connecting…
</span>
)
}
if (!isConnected) {
return (
<button
type="button"
onClick={show}
className="inline-flex h-8 items-center rounded-md bg-neutral-900 px-3.5 text-[11px] font-medium text-neutral-100 transition hover:bg-black dark:bg-neutral-100 dark:text-neutral-900 dark:hover:bg-white"
>
Connect wallet
</button>
)
}
const label = ensName ?? truncate(address)
return (
<button
type="button"
onClick={show}
className="inline-flex h-8 items-center gap-2 rounded-md border border-neutral-200 bg-neutral-50 px-2.5 text-[11px] text-neutral-800 transition hover:border-neutral-300 hover:bg-neutral-100 dark:border-neutral-800 dark:bg-neutral-900/60 dark:text-neutral-200 dark:hover:border-neutral-700 dark:hover:bg-neutral-900"
aria-label="Wallet menu"
>
<span
className="h-1.5 w-1.5 rounded-full bg-emerald-500/90 dark:bg-emerald-400/80"
aria-hidden="true"
/>
<span className="font-mono">{label}</span>
</button>
)
}}
</ConnectKitButton.Custom>
<button
type="button"
onClick={openConnectKit}
className="inline-flex h-8 items-center gap-2 rounded-md border border-neutral-200 bg-neutral-50 px-2.5 text-[11px] text-neutral-800 transition hover:border-neutral-300 hover:bg-neutral-100 dark:border-neutral-800 dark:bg-neutral-900/60 dark:text-neutral-200 dark:hover:border-neutral-700 dark:hover:bg-neutral-900"
aria-label="Wallet menu"
>
<span
className="h-1.5 w-1.5 rounded-full bg-emerald-500/90 dark:bg-emerald-400/80"
aria-hidden="true"
/>
<span className="font-mono">{label}</span>
</button>
)
}

function truncate(addr: string | undefined): string {
if (!addr) return ''
return `${addr.slice(0, 6)}${addr.slice(-4)}`
return `${addr.slice(0, 6)}...${addr.slice(-4)}`
}
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@
},
"packageManager": "pnpm@10.33.2+sha512.a90faf6feeab71ad6c6e57f94e0fe1a12f5dcc22cd754db40ae9593eb6a3e0b6b12e3540218bb37ae083404b1f2ce6db2a4121e979829b4aff94b99f49da1cf8",
"scripts": {
"build": "pnpm -r --filter './packages/*' build",
"test": "pnpm -r --filter './packages/*' test",
"build": "pnpm -r --filter \"./packages/*\" --filter @usdh-kit-apps/demo build",
"test": "pnpm -r --filter \"./packages/*\" test",
"lint": "biome check .",
"lint:fix": "biome check --write .",
"typecheck": "pnpm -r --filter './packages/*' typecheck",
"typecheck": "pnpm -r --filter \"./packages/*\" --filter @usdh-kit-apps/demo typecheck",
"verify": "pnpm lint && pnpm build && pnpm typecheck && pnpm test && pnpm smoke:consumer",
"smoke:consumer": "node scripts/consumer-smoke.mjs",
"changeset": "changeset",
"release": "changeset publish",
"version-packages": "changeset version",
Expand Down
11 changes: 3 additions & 8 deletions packages/sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,9 @@
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"./package.json": "./package.json"
},
Expand Down
36 changes: 36 additions & 0 deletions packages/sdk/test/package-exports.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { readFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { describe, expect, it } from 'vitest'

interface PackageJson {
exports?: {
'.'?: {
types?: string
import?: string
require?: string
}
}
types?: string
main?: string
module?: string
}

describe('package exports', () => {
it('exposes root types for TypeScript bundler consumers', () => {
const packageJson = readPackageJson()
const rootExport = packageJson.exports?.['.']

expect(packageJson.types).toBe('./dist/index.d.ts')
expect(packageJson.main).toBe('./dist/index.cjs')
expect(packageJson.module).toBe('./dist/index.js')
expect(rootExport).toEqual({
types: './dist/index.d.ts',
import: './dist/index.js',
require: './dist/index.cjs',
})
})
})

function readPackageJson(): PackageJson {
return JSON.parse(readFileSync(resolve(__dirname, '../package.json'), 'utf8')) as PackageJson
}
2 changes: 2 additions & 0 deletions packages/widget/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ separately only when your app imports SDK APIs directly.

The widget reads the connected wallet from wagmi. Wrap your tree in `WagmiProvider` and `QueryClientProvider` (e.g. via ConnectKit or RainbowKit) before rendering it.

The root widget entry is ESM-only because the React wallet stack it composes is ESM-first. CommonJS projects can still load `@usdh-kit/widget/styles.css` and `@usdh-kit/widget/tailwind-content`, but should import the widget from an ESM module or through their app bundler.

```tsx
import { USDHSwap } from '@usdh-kit/widget'
import '@usdh-kit/widget/styles.css'
Expand Down
11 changes: 2 additions & 9 deletions packages/widget/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,12 @@
},
"keywords": ["hyperliquid", "usdh", "react", "widget", "swap", "defi"],
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
},
"./styles.css": "./dist/styles.css",
"./tailwind-content": {
Expand Down
4 changes: 2 additions & 2 deletions packages/widget/tailwind-content.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ const path = require('node:path')
* (Tailwind v3 does not deep-merge `content` arrays from presets, so this
* is exposed as a plain array instead of a preset object.)
*/
const widgetDir = path.dirname(require.resolve('@usdh-kit/widget'))
const widgetDir = __dirname.replaceAll(path.sep, path.posix.sep)

module.exports = [`${widgetDir}/**/*.{js,cjs,mjs}`]
module.exports = [`${widgetDir}/dist/**/*.{js,mjs}`]
46 changes: 46 additions & 0 deletions packages/widget/test/package-exports.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { readFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { describe, expect, it } from 'vitest'

interface PackageJson {
exports?: {
'.'?: {
types?: string
import?: string
require?: string
}
'./styles.css'?: string
'./tailwind-content'?: {
types?: string
default?: string
}
}
types?: string
main?: string
module?: string
}

describe('package exports', () => {
it('exposes an ESM widget root and CJS-safe secondary entries', () => {
const packageJson = readPackageJson()
const rootExport = packageJson.exports?.['.']

expect(packageJson.types).toBe('./dist/index.d.ts')
expect(packageJson.main).toBeUndefined()
expect(packageJson.module).toBe('./dist/index.js')
expect(rootExport).toEqual({
types: './dist/index.d.ts',
import: './dist/index.js',
})
expect(rootExport?.require).toBeUndefined()
expect(packageJson.exports?.['./styles.css']).toBe('./dist/styles.css')
expect(packageJson.exports?.['./tailwind-content']).toEqual({
types: './tailwind-content.d.cts',
default: './tailwind-content.cjs',
})
})
})

function readPackageJson(): PackageJson {
return JSON.parse(readFileSync(resolve(__dirname, '../package.json'), 'utf8')) as PackageJson
}
2 changes: 1 addition & 1 deletion packages/widget/tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { defineConfig } from 'tsup'

export default defineConfig({
entry: ['src/index.ts'],
format: ['esm', 'cjs'],
format: ['esm'],
dts: true,
clean: true,
treeshake: 'safest',
Expand Down
Loading
Loading