sse-hooks 1.2.0 → 2.0.0

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/dist/cli.mjs ADDED
@@ -0,0 +1,373 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.ts
4
+ import { Command } from "commander";
5
+
6
+ // src/cli/commands/add.ts
7
+ import inquirer from "inquirer";
8
+
9
+ // src/cli/utils/config.ts
10
+ import fs from "fs-extra";
11
+ import path from "path";
12
+
13
+ // src/cli/utils/logger.ts
14
+ import chalk from "chalk";
15
+ var error = (message) => {
16
+ console.error(chalk.red(`\u274C ${message}`));
17
+ };
18
+ var success = (message) => {
19
+ console.log(chalk.green(`\u2705 ${message}`));
20
+ };
21
+ var info = (message) => {
22
+ console.log(chalk.blue(`\u2139 ${message}`));
23
+ };
24
+ var warn = (message) => {
25
+ console.warn(chalk.yellow(`\u26A0 ${message}`));
26
+ };
27
+
28
+ // src/cli/utils/constants.ts
29
+ var ORIGIN = "https://sse-hooks.vercel.app";
30
+ var REGISTRY_CONFIG = {
31
+ ORIGIN,
32
+ BASE_URL: `${ORIGIN}/api/registry/hook`,
33
+ MANIFEST_ENDPOINT: "manifest.json",
34
+ HOOK_ENDPOINT: (name) => `${name}/meta.json`,
35
+ ERRORS: {
36
+ FETCH_FAILED: "Failed to fetch from registry",
37
+ MANIFEST_NOT_FOUND: "Registry manifest not found",
38
+ HOOK_NOT_FOUND: "Hook not found in registry"
39
+ }
40
+ };
41
+ var DEFAULT_CONFIG_VALUES = {
42
+ HOOKS_DIR: "src/hooks",
43
+ REPO_URL: REGISTRY_CONFIG.BASE_URL
44
+ };
45
+ var CLI = {
46
+ COMMAND_NAME: "sse-tools",
47
+ CONFIG_FILE: "sse.config.json",
48
+ COMMANDS: {
49
+ INIT: "init",
50
+ ADD: "add",
51
+ LIST: "list"
52
+ },
53
+ PROMPTS: {
54
+ HOOKS_DIR: "Where should hooks be stored?",
55
+ DEFAULT_LANG: "Default hook language?",
56
+ REPO_URL: "Registry API URL?"
57
+ }
58
+ };
59
+
60
+ // src/cli/utils/config.ts
61
+ async function getConfig() {
62
+ try {
63
+ const configPath = path.resolve(CLI.CONFIG_FILE);
64
+ if (!await fs.pathExists(configPath)) {
65
+ warn(
66
+ `Configuration not found. Run "${CLI.COMMAND_NAME} ${CLI.COMMANDS.INIT}" first.`
67
+ );
68
+ throw new Error("Configuration missing");
69
+ }
70
+ return await fs.readJson(configPath);
71
+ } catch (err) {
72
+ error("Failed to read configuration");
73
+ throw err;
74
+ }
75
+ }
76
+ async function saveConfig(config) {
77
+ try {
78
+ await fs.writeJson(CLI.CONFIG_FILE, config, { spaces: 2 });
79
+ } catch (err) {
80
+ error("Failed to save configuration");
81
+ throw err;
82
+ }
83
+ }
84
+
85
+ // src/cli/utils/registry.ts
86
+ import axios from "axios";
87
+ import fs2 from "fs-extra";
88
+ import path2 from "path";
89
+ async function fetchHookList(registryUrl) {
90
+ try {
91
+ const baseUrl = registryUrl.replace(/\/$/, "");
92
+ const manifestUrl = `${baseUrl}/${REGISTRY_CONFIG.MANIFEST_ENDPOINT}`;
93
+ info(`Fetching hook list...`);
94
+ const response = await axios.get(manifestUrl);
95
+ if (response.data?.hooks && Array.isArray(response.data.hooks)) {
96
+ return response.data.hooks;
97
+ }
98
+ throw new Error(REGISTRY_CONFIG.ERRORS.MANIFEST_NOT_FOUND);
99
+ } catch (err) {
100
+ const message = err instanceof Error ? err.message : "Unknown error";
101
+ error("Failed to fetch hook list: " + message);
102
+ throw err;
103
+ }
104
+ }
105
+ async function downloadHook(registryUrl, hookNameOrUrl, language, filePath, metaOnly = false) {
106
+ try {
107
+ let hookUrl;
108
+ if (hookNameOrUrl.startsWith("http://") || hookNameOrUrl.startsWith("https://")) {
109
+ hookUrl = hookNameOrUrl;
110
+ } else {
111
+ const baseUrl = registryUrl.replace(/\/$/, "");
112
+ hookUrl = `${baseUrl}/${REGISTRY_CONFIG.HOOK_ENDPOINT(hookNameOrUrl)}`;
113
+ }
114
+ const response = await axios.get(hookUrl);
115
+ const metaData = response.data;
116
+ if (metaOnly) {
117
+ return metaData;
118
+ }
119
+ const code = language === "js" ? metaData.file.js : metaData.file.content;
120
+ if (!code) {
121
+ throw new Error(`Code for ${language.toUpperCase()} not found.`);
122
+ }
123
+ if (filePath) {
124
+ await fs2.ensureDir(path2.dirname(filePath));
125
+ await fs2.writeFile(filePath, code);
126
+ }
127
+ return metaData;
128
+ } catch (err) {
129
+ if (axios.isAxiosError(err) && err.response?.status === 404) {
130
+ error(`Hook "${hookNameOrUrl}" not found in registry.`);
131
+ throw new Error(REGISTRY_CONFIG.ERRORS.HOOK_NOT_FOUND);
132
+ }
133
+ const message = err instanceof Error ? err.message : "Unknown error";
134
+ error(`Failed to process hook: ${hookNameOrUrl} - ${message}`);
135
+ throw err;
136
+ }
137
+ }
138
+
139
+ // src/cli/commands/add.ts
140
+ import path4 from "path";
141
+ import fs4 from "fs-extra";
142
+ import chalk3 from "chalk";
143
+ import ora2 from "ora";
144
+
145
+ // src/cli/utils/npm.ts
146
+ import path3 from "path";
147
+ import fs3 from "fs/promises";
148
+ import { execSync } from "child_process";
149
+ import { error as error2 } from "console";
150
+ import chalk2 from "chalk";
151
+ import ora from "ora";
152
+ async function installNpmDependencies(dependencies = []) {
153
+ if (!dependencies.length) return;
154
+ const pkgPath = path3.join(process.cwd(), "package.json");
155
+ let pkg2;
156
+ try {
157
+ pkg2 = JSON.parse(await fs3.readFile(pkgPath, "utf-8"));
158
+ } catch {
159
+ error2("package.json not found. Skipping dependency installation.");
160
+ return;
161
+ }
162
+ const allDeps = { ...pkg2.dependencies, ...pkg2.devDependencies };
163
+ const missingDeps = dependencies.filter((dep) => !allDeps[dep]);
164
+ if (missingDeps.length > 0) {
165
+ const spinner = ora(
166
+ `Installing dependencies: ${chalk2.cyan(missingDeps.join(", "))}...`
167
+ ).start();
168
+ try {
169
+ const agent = process.env.npm_config_user_agent || "";
170
+ const command = agent.includes("pnpm") ? "pnpm add" : agent.includes("yarn") ? "yarn add" : "npm install";
171
+ execSync(`${command} ${missingDeps.join(" ")}`, { stdio: "ignore" });
172
+ spinner.succeed(chalk2.green("Dependencies installed successfully."));
173
+ } catch (err) {
174
+ spinner.fail(chalk2.red("Failed to install dependencies."));
175
+ }
176
+ }
177
+ }
178
+
179
+ // src/cli/commands/add.ts
180
+ async function addCommand(hookNames = [], options = {}) {
181
+ try {
182
+ const config = await getConfig();
183
+ const installDir = config.hooks.hooksDir;
184
+ const language = options?.language || config?.hooks.defaultLanguage;
185
+ let initialQueue = [...hookNames];
186
+ if (initialQueue.length === 0) {
187
+ const availableHooks = await fetchHookList(config.hooks.registryUrl);
188
+ const { selectedHooks } = await inquirer.prompt([
189
+ {
190
+ type: "checkbox",
191
+ name: "selectedHooks",
192
+ message: "Select hooks to install:",
193
+ pageSize: 10,
194
+ choices: availableHooks.map((hook) => ({
195
+ name: `${hook.name} - ${hook.description}`,
196
+ value: hook.name
197
+ })),
198
+ validate: (input) => input.length > 0 || "You must choose at least one hook"
199
+ }
200
+ ]);
201
+ initialQueue = selectedHooks;
202
+ }
203
+ const hooksToInstall = /* @__PURE__ */ new Set();
204
+ const dependenciesToInstall = /* @__PURE__ */ new Set();
205
+ const payloadMap = /* @__PURE__ */ new Map();
206
+ const resolveSpinner = ora2("Resolving hooks...").start();
207
+ try {
208
+ const queue = [...initialQueue];
209
+ while (queue.length > 0) {
210
+ const currentInput = queue.shift();
211
+ if (!currentInput.startsWith("http") && hooksToInstall.has(currentInput)) {
212
+ continue;
213
+ }
214
+ const meta = await downloadHook(
215
+ config.hooks.registryUrl,
216
+ currentInput,
217
+ language,
218
+ "",
219
+ true
220
+ );
221
+ const realName = meta.name;
222
+ if (hooksToInstall.has(realName)) continue;
223
+ hooksToInstall.add(realName);
224
+ payloadMap.set(realName, meta);
225
+ if (meta.registryDependencies) {
226
+ queue.push(...meta.registryDependencies);
227
+ }
228
+ if (meta.dependencies) {
229
+ meta.dependencies.forEach((dep) => dependenciesToInstall.add(dep));
230
+ }
231
+ }
232
+ resolveSpinner.stop();
233
+ } catch (err) {
234
+ resolveSpinner.fail("Failed to resolve hooks.");
235
+ error(err instanceof Error ? err.message : "Unknown error");
236
+ return;
237
+ }
238
+ const sortedHooks = Array.from(hooksToInstall).sort();
239
+ const sortedDeps = Array.from(dependenciesToInstall).sort();
240
+ if (sortedHooks.length) {
241
+ console.log(chalk3.bold.cyan(`
242
+ Hooks:`));
243
+ sortedHooks.forEach((h) => console.log(`- ${h}`));
244
+ }
245
+ if (sortedDeps.length) {
246
+ console.log(chalk3.bold.cyan(`
247
+ Dependencies:`));
248
+ sortedDeps.forEach((d) => console.log(`- ${d}`));
249
+ }
250
+ console.log("");
251
+ const { proceed } = await inquirer.prompt([
252
+ {
253
+ type: "confirm",
254
+ name: "proceed",
255
+ message: "Ready to install. Proceed?",
256
+ default: true
257
+ }
258
+ ]);
259
+ if (!proceed) {
260
+ info("Operation cancelled.");
261
+ return;
262
+ }
263
+ if (sortedDeps.length) {
264
+ await installNpmDependencies(sortedDeps);
265
+ }
266
+ const writeSpinner = ora2("Writing components...").start();
267
+ for (const name of sortedHooks) {
268
+ const meta = payloadMap.get(name);
269
+ const code = language === "js" ? meta.file.js : meta.file.content;
270
+ const fileName = `${meta.name}.${language}`;
271
+ const filePath = path4.join(installDir, fileName);
272
+ try {
273
+ await fs4.access(filePath);
274
+ } catch {
275
+ }
276
+ await fs4.ensureDir(path4.dirname(filePath));
277
+ await fs4.writeFile(filePath, code);
278
+ }
279
+ writeSpinner.succeed(chalk3.green("Done."));
280
+ if (hooksToInstall.size > 0) {
281
+ success(
282
+ `Installed ${hooksToInstall.size} component(s) to ${chalk3.cyan(
283
+ installDir
284
+ )}`
285
+ );
286
+ }
287
+ } catch (err) {
288
+ if (err.message === "Configuration missing") {
289
+ error('No configuration found. Please run "sse-tool init" first.');
290
+ } else {
291
+ error(err instanceof Error ? err.message : "An unknown error occurred");
292
+ }
293
+ }
294
+ }
295
+
296
+ // src/cli/commands/list.ts
297
+ import Table from "cli-table3";
298
+ async function listCommand() {
299
+ try {
300
+ const config = await getConfig();
301
+ const hooks = await fetchHookList(config.hooks.registryUrl);
302
+ const table = new Table({
303
+ head: ["Hook Name", "Description"],
304
+ colWidths: [25, 60]
305
+ });
306
+ hooks.forEach((hook) => {
307
+ table.push([hook.name, hook.description || "-"]);
308
+ });
309
+ console.log(table.toString());
310
+ info(`Total hooks: ${hooks.length}`);
311
+ } catch (err) {
312
+ error(
313
+ "Failed to list hooks: " + (err instanceof Error ? err.message : "Unknown error")
314
+ );
315
+ }
316
+ }
317
+
318
+ // src/cli/commands/init.ts
319
+ import inquirer2 from "inquirer";
320
+ import fs5 from "fs-extra";
321
+ import path5 from "path";
322
+ async function initCommand() {
323
+ let isTypeScriptProject = false;
324
+ try {
325
+ const pkgPath = path5.resolve("package.json");
326
+ if (await fs5.pathExists(pkgPath)) {
327
+ const pkg2 = await fs5.readJson(pkgPath);
328
+ const allDeps = { ...pkg2.dependencies, ...pkg2.devDependencies };
329
+ if (allDeps.typescript) {
330
+ isTypeScriptProject = true;
331
+ }
332
+ }
333
+ } catch (err) {
334
+ }
335
+ const answers = await inquirer2.prompt([
336
+ {
337
+ type: "input",
338
+ name: "hooksDir",
339
+ message: CLI.PROMPTS.HOOKS_DIR,
340
+ default: DEFAULT_CONFIG_VALUES.HOOKS_DIR
341
+ },
342
+ {
343
+ type: "list",
344
+ name: "defaultLanguage",
345
+ message: CLI.PROMPTS.DEFAULT_LANG,
346
+ choices: ["ts", "js"],
347
+ default: isTypeScriptProject ? "ts" : "js"
348
+ }
349
+ ]);
350
+ await saveConfig({
351
+ hooks: {
352
+ hooksDir: answers.hooksDir,
353
+ defaultLanguage: answers.defaultLanguage,
354
+ registryUrl: DEFAULT_CONFIG_VALUES.REPO_URL
355
+ }
356
+ });
357
+ success("Configuration saved!");
358
+ }
359
+
360
+ // src/cli.ts
361
+ import { readFileSync } from "fs";
362
+ var program = new Command();
363
+ var pkg = JSON.parse(
364
+ readFileSync(new URL("../package.json", import.meta.url), "utf-8")
365
+ );
366
+ program.name(pkg?.name || "sse-tool").description("CLI for managing custom React hooks").version(`${pkg?.version}`, "-v, --version", "Output the current version");
367
+ program.command("init").description("Initialize hooks configuration").action(initCommand);
368
+ program.command("add [hooks...]").description("Add one or more custom hooks").option("-l, --language <language>", "Specify hook language (js|ts)").option("-d, --dir <directory>", "Specify installation directory").action((hooks, options) => {
369
+ const hookNames = Array.isArray(hooks) ? hooks : hooks ? [hooks] : [];
370
+ addCommand(hookNames, options);
371
+ });
372
+ program.command("list").description("List available hooks").action(listCommand);
373
+ program.parse(process.argv);
package/package.json CHANGED
@@ -1,43 +1,29 @@
1
1
  {
2
2
  "name": "sse-hooks",
3
- "version": "1.2.0",
4
- "description": "React hook library, ready to use, written in Typescript.",
3
+ "version": "2.0.0",
4
+ "description": "Tools for SSE Hooks",
5
5
  "license": "MIT",
6
6
  "author": "SSE World",
7
- "type": "module",
7
+ "scripts": {
8
+ "build": "tsup"
9
+ },
8
10
  "files": [
9
- "dist"
11
+ "dist/**/*"
10
12
  ],
11
- "main": "./dist/index.mjs",
12
- "types": "./dist/index.d.ts",
13
- "exports": {
14
- "./package.json": "./package.json",
15
- ".": {
16
- "import": {
17
- "types": "./dist/index.d.mts",
18
- "default": "./dist/index.mjs"
19
- },
20
- "require": {
21
- "types": "./dist/index.d.cts",
22
- "default": "./dist/index.cjs"
23
- }
24
- }
25
- },
26
- "scripts": {
27
- "build": "unbuild",
28
- "build:index": "node ./scripts/generate-index.js"
13
+ "bin": {
14
+ "sse-hooks": "dist/cli.mjs"
29
15
  },
30
16
  "devDependencies": {
31
- "@types/lodash.debounce": "^4.0.9",
32
- "@types/node": "^25.1.0",
33
- "@types/react": "^19.2.2",
34
- "@types/react-dom": "^19.2.2",
35
- "typescript": "^5.9.2"
17
+ "@types/fs-extra": "^11.0.4",
18
+ "tsup": "^8.5.1"
36
19
  },
37
20
  "dependencies": {
38
- "lodash.debounce": "^4.0.8",
39
- "react": "^19.2.3",
40
- "react-dom": "^19.2.3",
41
- "unbuild": "^3.6.1"
21
+ "axios": "^1.13.4",
22
+ "chalk": "^4.1.2",
23
+ "cli-table3": "^0.6.5",
24
+ "commander": "^14.0.3",
25
+ "fs-extra": "^11.3.3",
26
+ "inquirer": "^13.2.2",
27
+ "ora": "^9.1.0"
42
28
  }
43
29
  }
package/README.md DELETED
@@ -1,48 +0,0 @@
1
- ## 🪝 Available Hooks
2
-
3
- <!-- HOOKS:START -->
4
-
5
- - [`useAudioRecorder`](/docs/use-audio-recorder) — A comprehensive hook for audio recording with real-time analysis using getUserMedia, MediaRecorder, and Web Audio APIs
6
- - [`useBoolean`](/docs/use-boolean) — handles boolean state with useful utility functions.
7
- - [`useClickAnyWhere`](/docs/use-click-any-where) — handles click events anywhere on the document.
8
- - [`useClickAway`](/docs/use-click-away) — triggers a callback when a user clicks outside the referenced element.
9
- It handles portal elements, scrollbar clicks, and touch interactions intelligently.
10
- - [`useCookie`](/docs/use-cookie) — manages state synchronized with a browser [cookie](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies).
11
- It handles serialization, prefixes, updates across tabs, and custom event synchronization.
12
- - [`useCopyToClipboard`](/docs/use-copy-to-clipboard) — copies text to the clipboard using the [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API).
13
- - [`useCountdown`](/docs/use-countdown) — manages countdown.
14
- - [`useCounter`](/docs/use-counter) — manages a counter with increment, decrement, reset, and setCount functionalities.
15
- - [`useDarkMode`](/docs/use-dark-mode) — returns the current state of the dark mode.
16
- - [`useDebounceCallback`](/docs/use-debounce-callback) — creates a debounced version of a callback function.
17
- - [`useDebounceValue`](/docs/use-debounce-value) — returns a debounced version of the provided value, along with a function to update it.
18
- - [`useDocumentTitle`](/docs/use-document-title) — sets the document title.
19
- - [`useEventCallback`](/docs/use-event-callback) — creates a memoized event callback.
20
- - [`useEventListener`](/docs/use-event-listener) —
21
- - [`useFetch`](/docs/use-fetch) — provides a wrapper around the native [fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) to handle HTTP requests with state management, abort capability, and TypeScript support.
22
- - [`useForkRef`](/docs/use-fork-ref) — Merges refs into a single memoized callback ref or null.
23
- - [`useHover`](/docs/use-hover) — tracks whether a DOM element is being hovered over.
24
- - [`useIndexedDB`](/docs/use-indexed-d-b) — provides an interface to the [IndexedDB API](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) for client-side storage of significant amounts of structured data.
25
- - [`useIntersectionObserver`](/docs/use-intersection-observer) — tracks the intersection of a DOM element with its containing element or the viewport using the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).
26
- - [`useInterval`](/docs/use-interval) — creates an interval that invokes a callback function at a specified delay using the [setInterval API](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval).
27
- - [`useIsClient`](/docs/use-is-client) — determines if the code is running on the client side (in the browser).
28
- - [`useIsMounted`](/docs/use-is-mounted) — determines if the component is currently mounted.
29
- - [`useLocalStorage`](/docs/use-local-storage) — uses the [localStorage API](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) to persist state across page reloads.
30
- - [`useMap`](/docs/use-map) — manages a key-value [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) state with setter actions.
31
- - [`useMediaQuery`](/docs/use-media-query) — tracks the state of a media query using the [Match Media API](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia).
32
- - [`useMediaSession`](/docs/use-media-session) — interacts with the [Media Session API](https://developer.mozilla.org/en-US/docs/Web/API/Media_Session_API).
33
- It allows you to customize media notifications and handle media control events (like play, pause, next track) from the system's notification area or lock screen.
34
- - [`useReadLocalStorage`](/docs/use-read-local-storage) —
35
- - [`useResizeObserver`](/docs/use-resize-observer) — observes the size of an element using the [ResizeObserver API](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver).
36
- - [`useScreen`](/docs/use-screen) —
37
- - [`useScript`](/docs/use-script) — dynamically loads scripts and tracking their loading status.
38
- - [`useScrollLock`](/docs/use-scroll-lock) — A custom hook that locks and unlocks scroll.
39
- - [`useSessionStorage`](/docs/use-session-storage) — uses the [sessionStorage API](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage) to persist state across page reloads.
40
- - [`useStep`](/docs/use-step) — manages and navigates between steps in a multi-step process.
41
- - [`useTernaryDarkMode`](/docs/use-ternary-dark-mode) — manages ternary (system, dark, light) dark mode with local storage support.
42
- - [`useTimeout`](/docs/use-timeout) — handles timeouts in React components using the [setTimeout API](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout).
43
- - [`useToggle`](/docs/use-toggle) — manages a boolean toggle state in React components.
44
- - [`useUnmount`](/docs/use-unmount) — runs a cleanup function when the component is unmounted.
45
- - [`useUserMedia`](/docs/use-user-media) — captures audio and video from the user's device.
46
- It handles permission errors, stream management, and cleanup automatically.
47
- - [`useWindowSize`](/docs/use-window-size) —
48
- <!-- HOOKS:END -->