From c942ae82ce9675f8cfbaa916b637a0db36a3ebf0 Mon Sep 17 00:00:00 2001 From: saifmohamedsv Date: Thu, 3 Sep 2026 00:02:51 +0300 Subject: [PATCH] docs(readme): fix broken bundle badge, add docs/CI/types badges, group hook index - Replace the chronically rate-limited bundlephobia badge (renders "rate limited by upstream service" on npm) with always-green npm/types + GitHub Actions CI-status badges; add a docs badge linking hookli.vercel.app. - Group the Available-hooks index by category (State / Effects / DOM / Data) with per-category counts, so npm/GitHub visitors can skim 65 hooks. - gen-readme.mjs emits the grouped index between HOOKS:START/END markers and fails loudly on a hook with an unknown category. Mirrors to the repo root. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01AuT9BTjvgatPxvkXSPSo1i --- README.md | 53 +++++++++++++++++--------- packages/hookli/README.md | 53 +++++++++++++++++--------- packages/hookli/scripts/gen-readme.mjs | 36 ++++++++++++----- 3 files changed, 97 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 15b5d15..a6f9f93 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,11 @@

npm version + Documentation + CI build status npm downloads per month total npm downloads - minzipped size + TypeScript types included GitHub stars Sponsor license @@ -86,6 +88,10 @@ function Component() { > ๐Ÿ“š Full docs with a **page per hook** and **live demos**: + + +### State (19) + - **[`useToggle`](https://hookli.vercel.app/docs/use-toggle)** โ€” Boolean state with toggle and explicit set. - **[`useForm`](https://hookli.vercel.app/docs/use-form)** โ€” Controlled form state with one change handler. - **[`useLocalStorage`](https://hookli.vercel.app/docs/use-local-storage)** โ€” State persisted to localStorage. @@ -99,6 +105,15 @@ function Component() { - **[`useStep`](https://hookli.vercel.app/docs/use-step)** โ€” 1-indexed step counter for wizards and steppers. - **[`useCountdown`](https://hookli.vercel.app/docs/use-countdown)** โ€” Self-stopping countdown or count-up timer. - **[`useMap`](https://hookli.vercel.app/docs/use-map)** โ€” Manage a Map as immutable React state. +- **[`usePrevious`](https://hookli.vercel.app/docs/use-previous)** โ€” Track a value from the previous render. +- **[`useList`](https://hookli.vercel.app/docs/use-list)** โ€” Array state with push, insert, update, remove, and clear helpers. +- **[`useSet`](https://hookli.vercel.app/docs/use-set)** โ€” Set state with add, remove, toggle, has, and clear helpers. +- **[`useQueue`](https://hookli.vercel.app/docs/use-queue)** โ€” FIFO queue state with add, remove, clear, and first/last/size. +- **[`useDefault`](https://hookli.vercel.app/docs/use-default)** โ€” useState that falls back to a default when the value is nullish. +- **[`useRafState`](https://hookli.vercel.app/docs/use-raf-state)** โ€” useState whose updates are batched to the next animation frame. + +### Effects (16) + - **[`useDebounce`](https://hookli.vercel.app/docs/use-debounce)** โ€” Debounces a changing value. - **[`useDebounceValue`](https://hookli.vercel.app/docs/use-debounce-value)** โ€” State whose debounced copy updates after a pause. - **[`useDebounceCallback`](https://hookli.vercel.app/docs/use-debounce-callback)** โ€” Debounces a callback, with cancel, flush and isPending. @@ -110,6 +125,14 @@ function Component() { - **[`useIsClient`](https://hookli.vercel.app/docs/use-is-client)** โ€” Reports false on the server and true after hydration. - **[`useIsMounted`](https://hookli.vercel.app/docs/use-is-mounted)** โ€” A stable getter for whether the component is still mounted. - **[`useDocumentTitle`](https://hookli.vercel.app/docs/use-document-title)** โ€” Keeps document.title in sync with a value, SSR-safe. +- **[`useThrottle`](https://hookli.vercel.app/docs/use-throttle)** โ€” Throttle a fast-changing value to at most one update per interval. +- **[`useUpdateEffect`](https://hookli.vercel.app/docs/use-update-effect)** โ€” A useEffect that skips the initial mount and runs only on updates. +- **[`useEffectOnce`](https://hookli.vercel.app/docs/use-effect-once)** โ€” Run an effect exactly once, on mount. +- **[`usePageVisibility`](https://hookli.vercel.app/docs/use-page-visibility)** โ€” Track whether the page/tab is currently visible. +- **[`useDeepCompareEffect`](https://hookli.vercel.app/docs/use-deep-compare-effect)** โ€” useEffect that compares dependencies by deep structural equality. + +### DOM (22) + - **[`useEventListener`](https://hookli.vercel.app/docs/use-event-listener)** โ€” Subscribe to a window, document or element event with cleanup. - **[`useClickOutside`](https://hookli.vercel.app/docs/use-click-outside)** โ€” Runs a callback on outside click. - **[`useMousePosition`](https://hookli.vercel.app/docs/use-mouse-position)** โ€” Cursor coordinates within an element. @@ -125,33 +148,27 @@ function Component() { - **[`useWindowSize`](https://hookli.vercel.app/docs/use-window-size)** โ€” Tracks the viewport's { width, height }, updated on resize. - **[`useCopyToClipboard`](https://hookli.vercel.app/docs/use-copy-to-clipboard)** โ€” Copy text to the clipboard, tracking the last copied value. - **[`useScript`](https://hookli.vercel.app/docs/use-script)** โ€” Load an external script and report its load status. -- **[`useFetch`](https://hookli.vercel.app/docs/use-fetch)** โ€” Declarative fetch with loading and error status. -- **[`useGeoLocation`](https://hookli.vercel.app/docs/use-geo-location)** โ€” Browser geolocation state. -- **[`usePrevious`](https://hookli.vercel.app/docs/use-previous)** โ€” Track a value from the previous render. -- **[`useList`](https://hookli.vercel.app/docs/use-list)** โ€” Array state with push, insert, update, remove, and clear helpers. -- **[`useSet`](https://hookli.vercel.app/docs/use-set)** โ€” Set state with add, remove, toggle, has, and clear helpers. -- **[`useThrottle`](https://hookli.vercel.app/docs/use-throttle)** โ€” Throttle a fast-changing value to at most one update per interval. -- **[`useUpdateEffect`](https://hookli.vercel.app/docs/use-update-effect)** โ€” A useEffect that skips the initial mount and runs only on updates. -- **[`useEffectOnce`](https://hookli.vercel.app/docs/use-effect-once)** โ€” Run an effect exactly once, on mount. - **[`useKeyPress`](https://hookli.vercel.app/docs/use-key-press)** โ€” Track whether a specific key is currently held down. - **[`useWindowScroll`](https://hookli.vercel.app/docs/use-window-scroll)** โ€” Track the window scroll position reactively. -- **[`useAsync`](https://hookli.vercel.app/docs/use-async)** โ€” Run an async function and track its loading, error, and value state. -- **[`useMutation`](https://hookli.vercel.app/docs/use-mutation)** โ€” Run an async write action on demand and track status, data, and error. -- **[`usePagination`](https://hookli.vercel.app/docs/use-pagination)** โ€” Page, page size, total pages, navigation helpers, and the current item range. -- **[`useNetworkState`](https://hookli.vercel.app/docs/use-network-state)** โ€” Track online/offline status and connection details. -- **[`usePageVisibility`](https://hookli.vercel.app/docs/use-page-visibility)** โ€” Track whether the page/tab is currently visible. - **[`useIdle`](https://hookli.vercel.app/docs/use-idle)** โ€” Detect user inactivity after a configurable threshold. -- **[`useQueue`](https://hookli.vercel.app/docs/use-queue)** โ€” FIFO queue state with add, remove, clear, and first/last/size. -- **[`useDefault`](https://hookli.vercel.app/docs/use-default)** โ€” useState that falls back to a default when the value is nullish. -- **[`useRafState`](https://hookli.vercel.app/docs/use-raf-state)** โ€” useState whose updates are batched to the next animation frame. -- **[`useDeepCompareEffect`](https://hookli.vercel.app/docs/use-deep-compare-effect)** โ€” useEffect that compares dependencies by deep structural equality. - **[`useTextSelection`](https://hookli.vercel.app/docs/use-text-selection)** โ€” Track the text the user has currently selected on the page. - **[`useLongPress`](https://hookli.vercel.app/docs/use-long-press)** โ€” Detect a long press (mouse or touch) via spreadable handlers. - **[`useHotkeys`](https://hookli.vercel.app/docs/use-hotkeys)** โ€” Bind a keyboard shortcut combo (e.g. ctrl+k) to a callback. - **[`useFullscreen`](https://hookli.vercel.app/docs/use-fullscreen)** โ€” Control the Fullscreen API for an element and track its state. + +### Data (8) + +- **[`useFetch`](https://hookli.vercel.app/docs/use-fetch)** โ€” Declarative fetch with loading and error status. +- **[`useGeoLocation`](https://hookli.vercel.app/docs/use-geo-location)** โ€” Browser geolocation state. +- **[`useAsync`](https://hookli.vercel.app/docs/use-async)** โ€” Run an async function and track its loading, error, and value state. +- **[`useMutation`](https://hookli.vercel.app/docs/use-mutation)** โ€” Run an async write action on demand and track status, data, and error. +- **[`usePagination`](https://hookli.vercel.app/docs/use-pagination)** โ€” Page, page size, total pages, navigation helpers, and the current item range. +- **[`useNetworkState`](https://hookli.vercel.app/docs/use-network-state)** โ€” Track online/offline status and connection details. - **[`useBattery`](https://hookli.vercel.app/docs/use-battery)** โ€” Read device battery level and charging state (where supported). - **[`usePermission`](https://hookli.vercel.app/docs/use-permission)** โ€” Query a Permissions API permission and track its state. + + ## ๐Ÿงช TypeScript Every hook ships its own declarations โ€” no `@types/hookli` needed. Data hooks are generic, diff --git a/packages/hookli/README.md b/packages/hookli/README.md index 383f534..2fc5ae8 100644 --- a/packages/hookli/README.md +++ b/packages/hookli/README.md @@ -13,9 +13,11 @@

npm version + Documentation + CI build status npm downloads per month total npm downloads - minzipped size + TypeScript types included GitHub stars Sponsor license @@ -84,6 +86,10 @@ function Component() { > ๐Ÿ“š Full docs with a **page per hook** and **live demos**: + + +### State (19) + - **[`useToggle`](https://hookli.vercel.app/docs/use-toggle)** โ€” Boolean state with toggle and explicit set. - **[`useForm`](https://hookli.vercel.app/docs/use-form)** โ€” Controlled form state with one change handler. - **[`useLocalStorage`](https://hookli.vercel.app/docs/use-local-storage)** โ€” State persisted to localStorage. @@ -97,6 +103,15 @@ function Component() { - **[`useStep`](https://hookli.vercel.app/docs/use-step)** โ€” 1-indexed step counter for wizards and steppers. - **[`useCountdown`](https://hookli.vercel.app/docs/use-countdown)** โ€” Self-stopping countdown or count-up timer. - **[`useMap`](https://hookli.vercel.app/docs/use-map)** โ€” Manage a Map as immutable React state. +- **[`usePrevious`](https://hookli.vercel.app/docs/use-previous)** โ€” Track a value from the previous render. +- **[`useList`](https://hookli.vercel.app/docs/use-list)** โ€” Array state with push, insert, update, remove, and clear helpers. +- **[`useSet`](https://hookli.vercel.app/docs/use-set)** โ€” Set state with add, remove, toggle, has, and clear helpers. +- **[`useQueue`](https://hookli.vercel.app/docs/use-queue)** โ€” FIFO queue state with add, remove, clear, and first/last/size. +- **[`useDefault`](https://hookli.vercel.app/docs/use-default)** โ€” useState that falls back to a default when the value is nullish. +- **[`useRafState`](https://hookli.vercel.app/docs/use-raf-state)** โ€” useState whose updates are batched to the next animation frame. + +### Effects (16) + - **[`useDebounce`](https://hookli.vercel.app/docs/use-debounce)** โ€” Debounces a changing value. - **[`useDebounceValue`](https://hookli.vercel.app/docs/use-debounce-value)** โ€” State whose debounced copy updates after a pause. - **[`useDebounceCallback`](https://hookli.vercel.app/docs/use-debounce-callback)** โ€” Debounces a callback, with cancel, flush and isPending. @@ -108,6 +123,14 @@ function Component() { - **[`useIsClient`](https://hookli.vercel.app/docs/use-is-client)** โ€” Reports false on the server and true after hydration. - **[`useIsMounted`](https://hookli.vercel.app/docs/use-is-mounted)** โ€” A stable getter for whether the component is still mounted. - **[`useDocumentTitle`](https://hookli.vercel.app/docs/use-document-title)** โ€” Keeps document.title in sync with a value, SSR-safe. +- **[`useThrottle`](https://hookli.vercel.app/docs/use-throttle)** โ€” Throttle a fast-changing value to at most one update per interval. +- **[`useUpdateEffect`](https://hookli.vercel.app/docs/use-update-effect)** โ€” A useEffect that skips the initial mount and runs only on updates. +- **[`useEffectOnce`](https://hookli.vercel.app/docs/use-effect-once)** โ€” Run an effect exactly once, on mount. +- **[`usePageVisibility`](https://hookli.vercel.app/docs/use-page-visibility)** โ€” Track whether the page/tab is currently visible. +- **[`useDeepCompareEffect`](https://hookli.vercel.app/docs/use-deep-compare-effect)** โ€” useEffect that compares dependencies by deep structural equality. + +### DOM (22) + - **[`useEventListener`](https://hookli.vercel.app/docs/use-event-listener)** โ€” Subscribe to a window, document or element event with cleanup. - **[`useClickOutside`](https://hookli.vercel.app/docs/use-click-outside)** โ€” Runs a callback on outside click. - **[`useMousePosition`](https://hookli.vercel.app/docs/use-mouse-position)** โ€” Cursor coordinates within an element. @@ -123,33 +146,27 @@ function Component() { - **[`useWindowSize`](https://hookli.vercel.app/docs/use-window-size)** โ€” Tracks the viewport's { width, height }, updated on resize. - **[`useCopyToClipboard`](https://hookli.vercel.app/docs/use-copy-to-clipboard)** โ€” Copy text to the clipboard, tracking the last copied value. - **[`useScript`](https://hookli.vercel.app/docs/use-script)** โ€” Load an external script and report its load status. -- **[`useFetch`](https://hookli.vercel.app/docs/use-fetch)** โ€” Declarative fetch with loading and error status. -- **[`useGeoLocation`](https://hookli.vercel.app/docs/use-geo-location)** โ€” Browser geolocation state. -- **[`usePrevious`](https://hookli.vercel.app/docs/use-previous)** โ€” Track a value from the previous render. -- **[`useList`](https://hookli.vercel.app/docs/use-list)** โ€” Array state with push, insert, update, remove, and clear helpers. -- **[`useSet`](https://hookli.vercel.app/docs/use-set)** โ€” Set state with add, remove, toggle, has, and clear helpers. -- **[`useThrottle`](https://hookli.vercel.app/docs/use-throttle)** โ€” Throttle a fast-changing value to at most one update per interval. -- **[`useUpdateEffect`](https://hookli.vercel.app/docs/use-update-effect)** โ€” A useEffect that skips the initial mount and runs only on updates. -- **[`useEffectOnce`](https://hookli.vercel.app/docs/use-effect-once)** โ€” Run an effect exactly once, on mount. - **[`useKeyPress`](https://hookli.vercel.app/docs/use-key-press)** โ€” Track whether a specific key is currently held down. - **[`useWindowScroll`](https://hookli.vercel.app/docs/use-window-scroll)** โ€” Track the window scroll position reactively. -- **[`useAsync`](https://hookli.vercel.app/docs/use-async)** โ€” Run an async function and track its loading, error, and value state. -- **[`useMutation`](https://hookli.vercel.app/docs/use-mutation)** โ€” Run an async write action on demand and track status, data, and error. -- **[`usePagination`](https://hookli.vercel.app/docs/use-pagination)** โ€” Page, page size, total pages, navigation helpers, and the current item range. -- **[`useNetworkState`](https://hookli.vercel.app/docs/use-network-state)** โ€” Track online/offline status and connection details. -- **[`usePageVisibility`](https://hookli.vercel.app/docs/use-page-visibility)** โ€” Track whether the page/tab is currently visible. - **[`useIdle`](https://hookli.vercel.app/docs/use-idle)** โ€” Detect user inactivity after a configurable threshold. -- **[`useQueue`](https://hookli.vercel.app/docs/use-queue)** โ€” FIFO queue state with add, remove, clear, and first/last/size. -- **[`useDefault`](https://hookli.vercel.app/docs/use-default)** โ€” useState that falls back to a default when the value is nullish. -- **[`useRafState`](https://hookli.vercel.app/docs/use-raf-state)** โ€” useState whose updates are batched to the next animation frame. -- **[`useDeepCompareEffect`](https://hookli.vercel.app/docs/use-deep-compare-effect)** โ€” useEffect that compares dependencies by deep structural equality. - **[`useTextSelection`](https://hookli.vercel.app/docs/use-text-selection)** โ€” Track the text the user has currently selected on the page. - **[`useLongPress`](https://hookli.vercel.app/docs/use-long-press)** โ€” Detect a long press (mouse or touch) via spreadable handlers. - **[`useHotkeys`](https://hookli.vercel.app/docs/use-hotkeys)** โ€” Bind a keyboard shortcut combo (e.g. ctrl+k) to a callback. - **[`useFullscreen`](https://hookli.vercel.app/docs/use-fullscreen)** โ€” Control the Fullscreen API for an element and track its state. + +### Data (8) + +- **[`useFetch`](https://hookli.vercel.app/docs/use-fetch)** โ€” Declarative fetch with loading and error status. +- **[`useGeoLocation`](https://hookli.vercel.app/docs/use-geo-location)** โ€” Browser geolocation state. +- **[`useAsync`](https://hookli.vercel.app/docs/use-async)** โ€” Run an async function and track its loading, error, and value state. +- **[`useMutation`](https://hookli.vercel.app/docs/use-mutation)** โ€” Run an async write action on demand and track status, data, and error. +- **[`usePagination`](https://hookli.vercel.app/docs/use-pagination)** โ€” Page, page size, total pages, navigation helpers, and the current item range. +- **[`useNetworkState`](https://hookli.vercel.app/docs/use-network-state)** โ€” Track online/offline status and connection details. - **[`useBattery`](https://hookli.vercel.app/docs/use-battery)** โ€” Read device battery level and charging state (where supported). - **[`usePermission`](https://hookli.vercel.app/docs/use-permission)** โ€” Query a Permissions API permission and track its state. + + ## ๐Ÿงช TypeScript Every hook ships its own declarations โ€” no `@types/hookli` needed. Data hooks are generic, diff --git a/packages/hookli/scripts/gen-readme.mjs b/packages/hookli/scripts/gen-readme.mjs index af2af74..58a29c4 100644 --- a/packages/hookli/scripts/gen-readme.mjs +++ b/packages/hookli/scripts/gen-readme.mjs @@ -1,6 +1,7 @@ -// Regenerate the README's "Available hooks" list + hook-count badge from the +// Regenerate the README's category-grouped hook index + hook-count badge from the // single source of truth: hooks.manifest.json. Run via `pnpm --filter hookli gen:manifest` -// (also runs on prepublishOnly). Do NOT hand-edit the generated list. +// (also runs on prepublishOnly). Do NOT hand-edit the generated index โ€” everything +// between the HOOKS:START / HOOKS:END markers is overwritten. import { readFileSync, writeFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; @@ -9,6 +10,10 @@ const root = join(dirname(fileURLToPath(import.meta.url)), ".."); const { hooks } = JSON.parse(readFileSync(join(root, "hooks.manifest.json"), "utf8")); const count = hooks.length; +// Category order + labels mirror apps/docs/lib/hooks-registry.ts (CATEGORY_ORDER / CATEGORY_LABELS). +const CATEGORY_ORDER = ["state", "effects", "dom", "data"]; +const CATEGORY_LABELS = { state: "State", effects: "Effects", dom: "DOM", data: "Data" }; + const readmePath = join(root, "README.md"); let md = readFileSync(readmePath, "utf8"); @@ -16,13 +21,26 @@ let md = readFileSync(readmePath, "utf8"); md = md.replace(/badge\/\d+_hooks-/g, `badge/${count}_hooks-`); md = md.replace(/alt="\d+ hooks"/g, `alt="${count} hooks"`); -// 2) the "Available hooks" bullet block (a contiguous run of `- **[`useX`](โ€ฆ)** โ€” โ€ฆ` lines) -const list = hooks - .map((h) => `- **[\`${h.name}\`](https://hookli.vercel.app/docs/${h.slug})** โ€” ${h.description}`) - .join("\n"); -const block = /(?:^- \*\*\[`use.*\r?\n?)+/m; -if (!block.test(md)) throw new Error("gen-readme: could not find the hook bullet block in README.md"); -md = md.replace(block, list + "\n"); +// 2) the category-grouped hook index, between the HOOKS:START / HOOKS:END markers. +// A hook whose category is not one we know would be silently dropped โ€” fail loudly. +const known = new Set(CATEGORY_ORDER); +const orphan = hooks.find((h) => !known.has(h.category)); +if (orphan) { + throw new Error(`gen-readme: hook "${orphan.name}" has unknown category "${orphan.category}"`); +} + +const bullet = (h) => + `- **[\`${h.name}\`](https://hookli.vercel.app/docs/${h.slug})** โ€” ${h.description}`; +const grouped = CATEGORY_ORDER.map((cat) => { + const items = hooks.filter((h) => h.category === cat); + return `### ${CATEGORY_LABELS[cat]} (${items.length})\n\n${items.map(bullet).join("\n")}`; +}).join("\n\n"); + +const region = /()[\s\S]*?()/; +if (!region.test(md)) { + throw new Error("gen-readme: could not find the HOOKS:START / HOOKS:END markers in README.md"); +} +md = md.replace(region, `$1\n\n${grouped}\n\n$2`); writeFileSync(readmePath, md);