@akb2/react-use-local-storage 1.0.0 → 1.1.1

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.
@@ -0,0 +1 @@
1
+ "use strict";var x=Object.create;var L=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var b=Object.getPrototypeOf,N=Object.prototype.hasOwnProperty;var V=(e,t)=>{for(var o in t)L(e,o,{get:t[o],enumerable:!0})},p=(e,t,o,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of D(t))!N.call(e,r)&&r!==o&&L(e,r,{get:()=>t[r],enumerable:!(i=y(t,r))||i.enumerable});return e};var h=(e,t,o)=>(o=e!=null?x(b(e)):{},p(t||!e||!e.__esModule?L(o,"default",{value:e,enumerable:!0}):o,e)),v=e=>p(L({},"__esModule",{value:!0}),e);var z={};V(z,{clearLocalStorage:()=>G,getLocalStorageValue:()=>d,removeLocalStorageValue:()=>C,setLocalStorageValue:()=>f,useLocalStorageState:()=>B});module.exports=v(z);var _=h(require("react"),1);var O=require("@akb2/types-tools");var T=require("@akb2/types-tools"),u=()=>typeof window>"u"?0:(0,T.anyToInt)(window.__AKB2_LOCAL_STORAGE__?.listeners?.size);var s=require("@akb2/types-tools");var a=(e,t)=>`${e}:{${t}}`;var c=e=>{e.storageArea===localStorage&&((0,s.isDefined)(window.__AKB2_LOCAL_STORAGE__)||(window.__AKB2_LOCAL_STORAGE__={}),(0,s.isDefined)(window.__AKB2_LOCAL_STORAGE__.listeners)||(window.__AKB2_LOCAL_STORAGE__.listeners=new Map),(0,s.isDefined)(window.__AKB2_LOCAL_STORAGE__.originalData)||(window.__AKB2_LOCAL_STORAGE__.originalData=new Map),(0,s.isDefined)(e.key)?((0,s.isDefined)(e.oldValue)&&e.oldValue!==e.newValue&&window.__AKB2_LOCAL_STORAGE__.originalData.delete(a(e.key,e.oldValue)),window.__AKB2_LOCAL_STORAGE__.listeners.get(e.key)?.forEach(t=>t())):(window.__AKB2_LOCAL_STORAGE__.originalData.clear(),window.__AKB2_LOCAL_STORAGE__.listeners.forEach(t=>t.forEach(o=>o()))))};var m=(e,t)=>{typeof window>"u"||(u()===0&&addEventListener("storage",c),(0,O.isDefined)(window.__AKB2_LOCAL_STORAGE__)||(window.__AKB2_LOCAL_STORAGE__={}),(0,O.isDefined)(window.__AKB2_LOCAL_STORAGE__.listeners)||(window.__AKB2_LOCAL_STORAGE__.listeners=new Map),window.__AKB2_LOCAL_STORAGE__.listeners.has(e)||window.__AKB2_LOCAL_STORAGE__.listeners.set(e,new Set),window.__AKB2_LOCAL_STORAGE__.listeners.get(e).add(t))};var E=(e,t)=>{typeof window>"u"||window.__AKB2_LOCAL_STORAGE__?.listeners?.has(e)&&(window.__AKB2_LOCAL_STORAGE__.listeners.get(e).delete(t),window.__AKB2_LOCAL_STORAGE__.listeners.get(e)?.size===0&&window.__AKB2_LOCAL_STORAGE__.listeners.delete(e),u()===0&&removeEventListener("storage",c))};var A=require("@akb2/types-tools");var K=require("@akb2/types-tools"),w=e=>(!(0,K.isDefined)(e)||typeof e!="object"||Object.isFrozen(e)||(Object.freeze(e),Object.values(e).forEach(w)),e);function d(e,t=null){let o=t??null;if(typeof window>"u")return o;let i=localStorage.getItem(e);if(!(0,A.isDefined)(i))return o;(0,A.isDefined)(window.__AKB2_LOCAL_STORAGE__)||(window.__AKB2_LOCAL_STORAGE__={}),(0,A.isDefined)(window.__AKB2_LOCAL_STORAGE__.originalData)||(window.__AKB2_LOCAL_STORAGE__.originalData=new Map);try{let r=a(e,i),l=window.__AKB2_LOCAL_STORAGE__.originalData.get(r);if((0,A.isDefined)(l))return l;let S=w(JSON.parse(i).value);return(0,A.isDefined)(S)&&window.__AKB2_LOCAL_STORAGE__.originalData.set(r,S),S}catch{return i.length>0?i:o}}var n=require("@akb2/types-tools");var f=(e,t)=>{if(typeof window>"u")return;let o=typeof t=="function"?t(d(e)):t;if(typeof o=="function")throw new Error("Functional updates are not supported in useLocalStorageState setter. Please provide the new value directly.");let i=localStorage.getItem(e)??null,r=(0,n.isDefined)(o)?JSON.stringify({value:o}):null;(0,n.isDefined)(r)?localStorage.setItem(e,r):localStorage.removeItem(e),i!==r&&((0,n.isDefined)(window.__AKB2_LOCAL_STORAGE__)||(window.__AKB2_LOCAL_STORAGE__={}),(0,n.isDefined)(window.__AKB2_LOCAL_STORAGE__.listeners)||(window.__AKB2_LOCAL_STORAGE__.listeners=new Map),(0,n.isDefined)(window.__AKB2_LOCAL_STORAGE__.originalData)||(window.__AKB2_LOCAL_STORAGE__.originalData=new Map),window.__AKB2_LOCAL_STORAGE__.listeners.get(e)?.forEach(l=>l()),(0,n.isDefined)(i)&&window.__AKB2_LOCAL_STORAGE__.originalData.delete(a(e,i)),(0,n.isDefined)(r)&&window.__AKB2_LOCAL_STORAGE__.originalData.set(a(e,r),w(JSON.parse(r).value)))};var g=require("react");function B(e,t=null){let o=(0,g.useCallback)(f.bind(null,e),[e]);return[(0,g.useSyncExternalStore)(r=>typeof window>"u"?()=>{}:(m(e,r),()=>E(e,r)),()=>d(e,t),()=>t),o,e]}var R=require("@akb2/types-tools"),G=()=>{if(typeof window>"u")return;let t=Array.from(window.__AKB2_LOCAL_STORAGE__?.listeners?.keys()??[]).reduce((o,i)=>(o.set(i,localStorage.getItem(i)??null),o),new Map);localStorage.clear(),window.__AKB2_LOCAL_STORAGE__?.originalData?.clear(),window.__AKB2_LOCAL_STORAGE__?.listeners?.forEach((o,i)=>{let r=t.get(i);(0,R.isDefined)(r)&&o.forEach(l=>l())})};var C=e=>f(e,null);0&&(module.exports={clearLocalStorage,getLocalStorageValue,removeLocalStorageValue,setLocalStorageValue,useLocalStorageState});
@@ -0,0 +1,41 @@
1
+ import { Nullable } from '@akb2/types-tools';
2
+ import { Dispatch, SetStateAction } from 'react';
3
+
4
+ /**
5
+ * A custom React hook that synchronizes a state variable with local storage.
6
+ *
7
+ * @template T The type of the state variable.
8
+ * @param key The key in local storage to associate with the state variable.
9
+ * @returns A tuple containing the state variable, a setter function, and the key.
10
+ */
11
+ declare function useLocalStorageState<T>(key: string): [Nullable<T>, Dispatch<Nullable<T>>, string];
12
+ declare function useLocalStorageState<T>(key: string, fallback: Exclude<T, null | undefined>): [Exclude<T, null | undefined>, Dispatch<Nullable<T>>, string];
13
+ declare function useLocalStorageState<T>(key: string, fallback: Nullable<T>): [Nullable<T>, Dispatch<Nullable<T>>, string];
14
+
15
+ declare global {
16
+ interface Window {
17
+ __AKB2_LOCAL_STORAGE__: {
18
+ listeners: Map<string, Set<() => void>>;
19
+ originalData: Map<string, unknown>;
20
+ };
21
+ }
22
+ }
23
+
24
+ declare const clearLocalStorage: () => void;
25
+
26
+ /**
27
+ * Retrieves the value associated with a specific key from local storage.
28
+ *
29
+ * @template T The expected type of the value.
30
+ * @param key The key in local storage to retrieve the value for.
31
+ * @returns The value associated with the key, or undefined if not found.
32
+ */
33
+ declare function getLocalStorageValue<T>(key: string): Nullable<T>;
34
+ declare function getLocalStorageValue<T>(key: string, fallback: Exclude<T, null | undefined>): Exclude<T, null | undefined>;
35
+ declare function getLocalStorageValue<T>(key: string, fallback: Nullable<T>): Nullable<T>;
36
+
37
+ declare const removeLocalStorageValue: (key: string) => void;
38
+
39
+ declare const setLocalStorageValue: <T>(key: string, valueOrCallback: SetStateAction<Nullable<T>>) => void;
40
+
41
+ export { clearLocalStorage, getLocalStorageValue, removeLocalStorageValue, setLocalStorageValue, useLocalStorageState };
@@ -0,0 +1,41 @@
1
+ import { Nullable } from '@akb2/types-tools';
2
+ import { Dispatch, SetStateAction } from 'react';
3
+
4
+ /**
5
+ * A custom React hook that synchronizes a state variable with local storage.
6
+ *
7
+ * @template T The type of the state variable.
8
+ * @param key The key in local storage to associate with the state variable.
9
+ * @returns A tuple containing the state variable, a setter function, and the key.
10
+ */
11
+ declare function useLocalStorageState<T>(key: string): [Nullable<T>, Dispatch<Nullable<T>>, string];
12
+ declare function useLocalStorageState<T>(key: string, fallback: Exclude<T, null | undefined>): [Exclude<T, null | undefined>, Dispatch<Nullable<T>>, string];
13
+ declare function useLocalStorageState<T>(key: string, fallback: Nullable<T>): [Nullable<T>, Dispatch<Nullable<T>>, string];
14
+
15
+ declare global {
16
+ interface Window {
17
+ __AKB2_LOCAL_STORAGE__: {
18
+ listeners: Map<string, Set<() => void>>;
19
+ originalData: Map<string, unknown>;
20
+ };
21
+ }
22
+ }
23
+
24
+ declare const clearLocalStorage: () => void;
25
+
26
+ /**
27
+ * Retrieves the value associated with a specific key from local storage.
28
+ *
29
+ * @template T The expected type of the value.
30
+ * @param key The key in local storage to retrieve the value for.
31
+ * @returns The value associated with the key, or undefined if not found.
32
+ */
33
+ declare function getLocalStorageValue<T>(key: string): Nullable<T>;
34
+ declare function getLocalStorageValue<T>(key: string, fallback: Exclude<T, null | undefined>): Exclude<T, null | undefined>;
35
+ declare function getLocalStorageValue<T>(key: string, fallback: Nullable<T>): Nullable<T>;
36
+
37
+ declare const removeLocalStorageValue: (key: string) => void;
38
+
39
+ declare const setLocalStorageValue: <T>(key: string, valueOrCallback: SetStateAction<Nullable<T>>) => void;
40
+
41
+ export { clearLocalStorage, getLocalStorageValue, removeLocalStorageValue, setLocalStorageValue, useLocalStorageState };
package/.dist/index.js ADDED
@@ -0,0 +1 @@
1
+ import*as _ from"react";import{isDefined as g}from"@akb2/types-tools";import{anyToInt as p}from"@akb2/types-tools";var L=()=>typeof window>"u"?0:p(window.__AKB2_LOCAL_STORAGE__?.listeners?.size);import{isDefined as s}from"@akb2/types-tools";var n=(e,t)=>`${e}:{${t}}`;var u=e=>{e.storageArea===localStorage&&(s(window.__AKB2_LOCAL_STORAGE__)||(window.__AKB2_LOCAL_STORAGE__={}),s(window.__AKB2_LOCAL_STORAGE__.listeners)||(window.__AKB2_LOCAL_STORAGE__.listeners=new Map),s(window.__AKB2_LOCAL_STORAGE__.originalData)||(window.__AKB2_LOCAL_STORAGE__.originalData=new Map),s(e.key)?(s(e.oldValue)&&e.oldValue!==e.newValue&&window.__AKB2_LOCAL_STORAGE__.originalData.delete(n(e.key,e.oldValue)),window.__AKB2_LOCAL_STORAGE__.listeners.get(e.key)?.forEach(t=>t())):(window.__AKB2_LOCAL_STORAGE__.originalData.clear(),window.__AKB2_LOCAL_STORAGE__.listeners.forEach(t=>t.forEach(o=>o()))))};var S=(e,t)=>{typeof window>"u"||(L()===0&&addEventListener("storage",u),g(window.__AKB2_LOCAL_STORAGE__)||(window.__AKB2_LOCAL_STORAGE__={}),g(window.__AKB2_LOCAL_STORAGE__.listeners)||(window.__AKB2_LOCAL_STORAGE__.listeners=new Map),window.__AKB2_LOCAL_STORAGE__.listeners.has(e)||window.__AKB2_LOCAL_STORAGE__.listeners.set(e,new Set),window.__AKB2_LOCAL_STORAGE__.listeners.get(e).add(t))};var O=(e,t)=>{typeof window>"u"||window.__AKB2_LOCAL_STORAGE__?.listeners?.has(e)&&(window.__AKB2_LOCAL_STORAGE__.listeners.get(e).delete(t),window.__AKB2_LOCAL_STORAGE__.listeners.get(e)?.size===0&&window.__AKB2_LOCAL_STORAGE__.listeners.delete(e),L()===0&&removeEventListener("storage",u))};import{isDefined as d}from"@akb2/types-tools";import{isDefined as T}from"@akb2/types-tools";var A=e=>(!T(e)||typeof e!="object"||Object.isFrozen(e)||(Object.freeze(e),Object.values(e).forEach(A)),e);function f(e,t=null){let o=t??null;if(typeof window>"u")return o;let r=localStorage.getItem(e);if(!d(r))return o;d(window.__AKB2_LOCAL_STORAGE__)||(window.__AKB2_LOCAL_STORAGE__={}),d(window.__AKB2_LOCAL_STORAGE__.originalData)||(window.__AKB2_LOCAL_STORAGE__.originalData=new Map);try{let i=n(e,r),l=window.__AKB2_LOCAL_STORAGE__.originalData.get(i);if(d(l))return l;let c=A(JSON.parse(r).value);return d(c)&&window.__AKB2_LOCAL_STORAGE__.originalData.set(i,c),c}catch{return r.length>0?r:o}}import{isDefined as a}from"@akb2/types-tools";var w=(e,t)=>{if(typeof window>"u")return;let o=typeof t=="function"?t(f(e)):t;if(typeof o=="function")throw new Error("Functional updates are not supported in useLocalStorageState setter. Please provide the new value directly.");let r=localStorage.getItem(e)??null,i=a(o)?JSON.stringify({value:o}):null;a(i)?localStorage.setItem(e,i):localStorage.removeItem(e),r!==i&&(a(window.__AKB2_LOCAL_STORAGE__)||(window.__AKB2_LOCAL_STORAGE__={}),a(window.__AKB2_LOCAL_STORAGE__.listeners)||(window.__AKB2_LOCAL_STORAGE__.listeners=new Map),a(window.__AKB2_LOCAL_STORAGE__.originalData)||(window.__AKB2_LOCAL_STORAGE__.originalData=new Map),window.__AKB2_LOCAL_STORAGE__.listeners.get(e)?.forEach(l=>l()),a(r)&&window.__AKB2_LOCAL_STORAGE__.originalData.delete(n(e,r)),a(i)&&window.__AKB2_LOCAL_STORAGE__.originalData.set(n(e,i),A(JSON.parse(i).value)))};import{useCallback as m,useSyncExternalStore as E}from"react";function K(e,t=null){let o=m(w.bind(null,e),[e]);return[E(i=>typeof window>"u"?()=>{}:(S(e,i),()=>O(e,i)),()=>f(e,t),()=>t),o,e]}import{isDefined as B}from"@akb2/types-tools";var R=()=>{if(typeof window>"u")return;let t=Array.from(window.__AKB2_LOCAL_STORAGE__?.listeners?.keys()??[]).reduce((o,r)=>(o.set(r,localStorage.getItem(r)??null),o),new Map);localStorage.clear(),window.__AKB2_LOCAL_STORAGE__?.originalData?.clear(),window.__AKB2_LOCAL_STORAGE__?.listeners?.forEach((o,r)=>{let i=t.get(r);B(i)&&o.forEach(l=>l())})};var G=e=>w(e,null);export{R as clearLocalStorage,f as getLocalStorageValue,G as removeLocalStorageValue,w as setLocalStorageValue,K as useLocalStorageState};
package/README.md CHANGED
@@ -18,54 +18,69 @@ The package provides ESM, CommonJS, and TypeScript declarations.
18
18
  import { useLocalStorageState } from "react-use-local-storage";
19
19
 
20
20
  export const Counter = () => {
21
- const [count, setCount] = useLocalStorageState<number>("counter");
21
+ const [count, setCount] = useLocalStorageState<number>("counter", 0);
22
22
 
23
23
  return (
24
- <button onClick={() => setCount((previous) => (previous ?? 0) + 1)}>
25
- Clicks: {count ?? 0}
24
+ <button onClick={() => setCount(count + 1)}>
25
+ Clicks: {count}
26
26
  </button>
27
27
  );
28
28
  };
29
29
  ```
30
30
 
31
- The hook returns `undefined` for a missing key. There is no second argument for an initial value. Use `??` to provide a display fallback; this does not write the fallback to storage.
31
+ The optional second argument is a fallback for a missing key. Without a fallback, a missing key returns `null`. The fallback is used for reading and rendering only; it is not written to storage. Removing the key makes the hook return its fallback again.
32
32
 
33
33
  ## API
34
34
 
35
- ### `useLocalStorageState<T>(key)`
35
+ ### `useLocalStorageState<T>(key, fallback?)`
36
36
 
37
37
  ```ts
38
- const [value, setValue, storageKey] = useLocalStorageState<string>("name");
38
+ const [value, setValue, storageKey] = useLocalStorageState<string>("name", "Guest");
39
39
  ```
40
40
 
41
41
  | Element | Description |
42
42
  | --- | --- |
43
- | `value` | The current value, or `undefined` for a missing key |
44
- | `setValue` | Writes a value or computes a new value from the previous one |
43
+ | `value` | The stored value, or the fallback for a missing key (`null` by default) |
44
+ | `setValue` | Writes a new value; accepts nullable values for removal |
45
45
  | `storageKey` | The key passed to the hook |
46
46
 
47
47
  ```ts
48
48
  setValue("Andrew");
49
- setValue((previous) => `${previous ?? ""}!`);
49
+ setValue(`${value}!`);
50
+ setValue(null); // Removes the key; this hook returns "Guest" again.
50
51
  ```
51
52
 
52
- An updater callback receives the current value from storage. At runtime, this can be `undefined` when the key is missing, so handle that case in the callback. The current setter signature is `Dispatch<SetStateAction<T>>`, which does not reflect this possible `undefined` argument.
53
+ The hook's setter is typed as `Dispatch<Nullable<T>>`: pass a value directly. Functional updater callbacks are not part of this public hook signature.
53
54
 
54
- Functions cannot be stored as values: a function argument is treated as an updater, and returning a function from that updater throws an error.
55
+ A non-null fallback selects an overload with a non-null return type. This is a TypeScript declaration, not runtime validation of existing storage data.
55
56
 
56
- ### `getLocalStorageValue<T>(key)`
57
+ For object or array fallbacks, reuse a stable reference so that repeated snapshot reads return the same value when the key is missing:
58
+
59
+ ```ts
60
+ const DEFAULT_PREFERENCES = { theme: "light" as const };
61
+
62
+ // Inside a component:
63
+ const [preferences, setPreferences] = useLocalStorageState<{
64
+ theme: "light" | "dark";
65
+ }>("preferences", DEFAULT_PREFERENCES);
66
+ ```
67
+
68
+ Avoid passing a newly created object or array as the fallback on every render, especially during server rendering and hydration.
69
+
70
+ ### `getLocalStorageValue<T>(key, fallback?)`
57
71
 
58
72
  Reads a value without creating a React subscription:
59
73
 
60
74
  ```ts
61
75
  import { getLocalStorageValue } from "react-use-local-storage";
62
76
 
63
- const name = getLocalStorageValue<string>("name");
77
+ const name = getLocalStorageValue<string>("name", "Guest");
78
+ const missing = getLocalStorageValue<string>("missing"); // null if absent
64
79
  ```
65
80
 
66
- Returns `undefined` for a missing key. The generic type `T` describes the expected value; it does not validate stored data at runtime.
81
+ Returns the fallback when the key is missing or `window` is unavailable. An omitted, `null`, or `undefined` fallback is normalized to `null` by the getter. The generic type `T` describes the expected value; it does not validate stored data at runtime.
67
82
 
68
- ### `setLocalStorageValue<T>(key, valueOrCallback)`
83
+ ### `setLocalStorageValue<T>(key, value)`
69
84
 
70
85
  Writes a value and notifies subscribers to that key in the current window:
71
86
 
@@ -73,7 +88,7 @@ Writes a value and notifies subscribers to that key in the current window:
73
88
  import { setLocalStorageValue } from "react-use-local-storage";
74
89
 
75
90
  setLocalStorageValue("name", "Andrew");
76
- setLocalStorageValue<number>("counter", (previous) => (previous ?? 0) + 1);
91
+ setLocalStorageValue<number>("counter", 1);
77
92
  ```
78
93
 
79
94
  Writing the same serialized content does not notify subscribers again. Passing `null` removes the value.
@@ -127,7 +142,7 @@ export const ThemeButton = () => {
127
142
  return (
128
143
  <button
129
144
  onClick={() =>
130
- setPreferences((previous) => ({ ...previous, theme: "dark" }))
145
+ setPreferences({ ...preferences, theme: "dark" })
131
146
  }
132
147
  >
133
148
  Theme: {preferences?.theme ?? "light"}
@@ -138,6 +153,19 @@ export const ThemeButton = () => {
138
153
 
139
154
  When reading data written by other code, the getter reads the `.value` property of parsed JSON. If parsing or subsequent processing throws, it returns the original nonempty string. Arbitrary JSON without the wrapper is not the library's storage format.
140
155
 
156
+ The fallback does not replace all invalid stored data:
157
+
158
+ | Stored content | Getter result |
159
+ | --- | --- |
160
+ | Missing key | Fallback, or `null` by default |
161
+ | `{"value":42}` | `42` |
162
+ | `{}` | `undefined`, even with a fallback |
163
+ | `{"value":null}` | `null`, even with a non-null fallback |
164
+ | Nonempty invalid JSON | The original raw string |
165
+ | Empty string | Fallback |
166
+
167
+ The getter currently returns `parsedData` directly after reading `.value`. Therefore, the non-null fallback overload does not guarantee a non-null runtime result for arbitrary existing data.
168
+
141
169
  ## Synchronization
142
170
 
143
171
  - In the current window, use the hook's setter or the library utilities to notify subscribers.
@@ -149,9 +177,9 @@ When writing directly from another tab, use the `JSON.stringify({ value: ... })`
149
177
 
150
178
  ## Server-side rendering
151
179
 
152
- The hook's server snapshot is `undefined`. When `window` is unavailable, reads return `undefined`, and writes and clearing are no-ops. After hydration, React uses the client snapshot from `localStorage`.
180
+ The hook's server snapshot is its fallback (`null` by default). When `window` is unavailable, the getter returns its fallback, and writes and clearing are no-ops. During hydration, use the same fallback on the server and client. After hydration, React uses the client snapshot from `localStorage`.
153
181
 
154
- Provide a fallback for the initial display, such as `value ?? ""`.
182
+ The fallback does not initialize or overwrite stored data.
155
183
 
156
184
  ## Limitations
157
185
 
@@ -194,4 +222,4 @@ This command builds the package and then publishes it with public access.
194
222
 
195
223
  ## License
196
224
 
197
- MPL-2.0. Author: akb2.
225
+ MPL-2.0. Author: akb2.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akb2/react-use-local-storage",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "A state hook with browser's localStorage",
5
5
  "exports": {
6
6
  ".": {
@@ -9,6 +9,9 @@
9
9
  "require": "./.dist/index.cjs"
10
10
  }
11
11
  },
12
+ "files": [
13
+ ".dist"
14
+ ],
12
15
  "keywords": [
13
16
  "react",
14
17
  "localStorage",
package/.gitignore DELETED
@@ -1,144 +0,0 @@
1
- # Logs
2
- logs
3
- *.log
4
- npm-debug.log*
5
- yarn-debug.log*
6
- yarn-error.log*
7
- lerna-debug.log*
8
-
9
- # Diagnostic reports (https://nodejs.org/api/report.html)
10
- report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11
-
12
- # Runtime data
13
- pids
14
- *.pid
15
- *.seed
16
- *.pid.lock
17
-
18
- # Directory for instrumented libs generated by jscoverage/JSCover
19
- lib-cov
20
-
21
- # Coverage directory used by tools like istanbul
22
- coverage
23
- *.lcov
24
-
25
- # nyc test coverage
26
- .nyc_output
27
-
28
- # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29
- .grunt
30
-
31
- # Bower dependency directory (https://bower.io/)
32
- bower_components
33
-
34
- # node-waf configuration
35
- .lock-wscript
36
-
37
- # Compiled binary addons (https://nodejs.org/api/addons.html)
38
- build/Release
39
-
40
- # Dependency directories
41
- node_modules/
42
- jspm_packages/
43
-
44
- # Snowpack dependency directory (https://snowpack.dev/)
45
- web_modules/
46
-
47
- # TypeScript cache
48
- *.tsbuildinfo
49
-
50
- # Optional npm cache directory
51
- .npm
52
-
53
- # Optional eslint cache
54
- .eslintcache
55
-
56
- # Optional stylelint cache
57
- .stylelintcache
58
-
59
- # Optional REPL history
60
- .node_repl_history
61
-
62
- # Output of 'npm pack'
63
- *.tgz
64
-
65
- # Yarn Integrity file
66
- .yarn-integrity
67
-
68
- # dotenv environment variable files
69
- .env
70
- .env.*
71
- !.env.example
72
-
73
- # parcel-bundler cache (https://parceljs.org/)
74
- .cache
75
- .parcel-cache
76
-
77
- # Next.js build output
78
- .next
79
- out
80
-
81
- # Nuxt.js build / generate output
82
- .nuxt
83
- dist
84
- .dist
85
- .output
86
-
87
- # Gatsby files
88
- .cache/
89
- # Comment in the public line in if your project uses Gatsby and not Next.js
90
- # https://nextjs.org/blog/next-9-1#public-directory-support
91
- # public
92
-
93
- # vuepress build output
94
- .vuepress/dist
95
-
96
- # vuepress v2.x temp directory
97
- .temp
98
-
99
- # Sveltekit cache directory
100
- .svelte-kit/
101
-
102
- # vitepress build output
103
- **/.vitepress/dist
104
-
105
- # vitepress cache directory
106
- **/.vitepress/cache
107
-
108
- # Docusaurus cache and generated files
109
- .docusaurus
110
-
111
- # Serverless directories
112
- .serverless/
113
-
114
- # FuseBox cache
115
- .fusebox/
116
-
117
- # DynamoDB Local files
118
- .dynamodb/
119
-
120
- # Firebase cache directory
121
- .firebase/
122
-
123
- # TernJS port file
124
- .tern-port
125
-
126
- # Stores VSCode versions used for testing VSCode extensions
127
- .vscode-test
128
-
129
- # pnpm
130
- .pnpm-store
131
-
132
- # yarn v3
133
- .pnp.*
134
- .yarn/*
135
- !.yarn/patches
136
- !.yarn/plugins
137
- !.yarn/releases
138
- !.yarn/sdks
139
- !.yarn/versions
140
-
141
- # Vite files
142
- vite.config.js.timestamp-*
143
- vite.config.ts.timestamp-*
144
- .vite/
package/.prettierignore DELETED
@@ -1,5 +0,0 @@
1
- **/node_modules/**
2
- **/.dist/**
3
- **/dist/**
4
- **/coverage/**
5
- pnpm-lock.yaml
package/.prettierrc.json DELETED
@@ -1,11 +0,0 @@
1
- {
2
- "semi": true,
3
- "singleQuote": false,
4
- "jsxSingleQuote": false,
5
- "tabWidth": 2,
6
- "useTabs": false,
7
- "trailingComma": "all",
8
- "arrowParens": "always",
9
- "printWidth": 100,
10
- "endOfLine": "lf"
11
- }
package/eslint.config.mjs DELETED
@@ -1,55 +0,0 @@
1
- import js from "@eslint/js";
2
- import stylistic from "@stylistic/eslint-plugin";
3
- import { defineConfig } from "eslint/config";
4
- import reactHooks from "eslint-plugin-react-hooks";
5
- import globals from "globals";
6
- import tseslint from "typescript-eslint";
7
- import prettier from "eslint-config-prettier/flat";
8
-
9
- export default defineConfig([
10
- {
11
- ignores: ["**/node_modules/**", "**/dist/**", "**/coverage/**"],
12
- },
13
- {
14
- files: ["**/*.{js,mjs,cjs,jsx,ts,tsx,mts,cts}"],
15
- extends: [js.configs.recommended],
16
- plugins: { "@stylistic": stylistic },
17
- languageOptions: {
18
- ecmaVersion: "latest",
19
- parserOptions: { ecmaFeatures: { jsx: true } },
20
- },
21
- rules: {
22
- eqeqeq: ["error", "always"],
23
- "prefer-const": "error",
24
- "no-var": "error",
25
- "prefer-arrow-callback": "error",
26
- "@stylistic/quotes": ["error", "double", { avoidEscape: true }],
27
- "@stylistic/jsx-quotes": ["error", "prefer-double"],
28
- },
29
- },
30
- {
31
- files: ["**/*.{ts,tsx,mts,cts}"],
32
- extends: [tseslint.configs.recommended],
33
- rules: {
34
- "@typescript-eslint/no-unused-vars": [
35
- "error",
36
- { argsIgnorePattern: "^_", caughtErrorsIgnorePattern: "^_" },
37
- ],
38
- },
39
- },
40
- {
41
- files: ["{backend,core}/**/*.{js,mjs,cjs,ts,mts,cts}", "**/*.config.{js,mjs,cjs,ts,mts,cts}"],
42
- languageOptions: { globals: globals.node },
43
- },
44
- {
45
- files: ["frontend/**/*.{js,jsx,ts,tsx}"],
46
- ignores: ["**/*.config.*"],
47
- languageOptions: { globals: globals.browser },
48
- plugins: { "react-hooks": reactHooks },
49
- rules: {
50
- "react-hooks/rules-of-hooks": "error",
51
- "react-hooks/exhaustive-deps": "warn",
52
- },
53
- },
54
- prettier,
55
- ]);
@@ -1,2 +0,0 @@
1
- allowBuilds:
2
- esbuild: false
@@ -1,33 +0,0 @@
1
- import { NotDefinable } from "@akb2/types-tools";
2
- import { addListenerByKey } from "@utils/add-listener-by-key";
3
- import { deleteListenerByKey } from "@utils/delete-listener-by-key";
4
- import { getLocalStorageValue } from "@utils/get-local-storage-value";
5
- import { setLocalStorageValue } from '@utils/set-local-storage-value';
6
- import type { Dispatch, SetStateAction } from 'react';
7
- import { useCallback, useSyncExternalStore } from 'react';
8
-
9
- /**
10
- * A custom React hook that synchronizes a state variable with local storage.
11
- *
12
- * @template T The type of the state variable.
13
- * @param key The key in local storage to associate with the state variable.
14
- * @returns A tuple containing the state variable, a setter function, and the key.
15
- */
16
- export const useLocalStorageState = <T>(key: string): [NotDefinable<T>, Dispatch<SetStateAction<T>>, string] => {
17
- const setState = useCallback(setLocalStorageValue.bind(null, key), [key]);
18
- const state = useSyncExternalStore(
19
- (onStoreChange) => {
20
- if (typeof window === 'undefined') {
21
- return (): void => {};
22
- }
23
-
24
- addListenerByKey(key, onStoreChange);
25
-
26
- return (): void => deleteListenerByKey(key, onStoreChange);
27
- },
28
- () => getLocalStorageValue<T>(key) as T,
29
- () => undefined,
30
- );
31
-
32
- return [state, setState, key];
33
- };
package/src/index.ts DELETED
@@ -1,6 +0,0 @@
1
- export { useLocalStorageState } from './hooks/use-local-storage-state';
2
- export * from './types/window';
3
- export { clearLocalStorage } from './utils/clear-local-storage';
4
- export { getLocalStorageValue } from './utils/get-local-storage-value';
5
- export { removeLocalStorageValue } from './utils/remove-local-storage-value';
6
- export { setLocalStorageValue } from './utils/set-local-storage-value';
@@ -1,3 +0,0 @@
1
- import * as React from 'react';
2
-
3
- export { React };
@@ -1,10 +0,0 @@
1
- declare global {
2
- interface Window {
3
- __AKB2_LOCAL_STORAGE__: {
4
- listeners: Map<string, Set<() => void>>;
5
- originalData: Map<string, unknown>;
6
- }
7
- }
8
- }
9
-
10
- export { };
@@ -1,34 +0,0 @@
1
- import { isDefined } from "@akb2/types-tools";
2
- import { getListenersKeysSize } from "@utils/get-listeners-keys-size";
3
- import { listenExternalStoreEvents } from "@utils/listen-external-store-events";
4
-
5
- /**
6
- * Adds a listener for changes to a specific key in local storage.
7
- *
8
- * @param key The key in local storage to listen for changes on.
9
- * @param listener The callback function to invoke when the specified key changes.
10
- * @returns void
11
- */
12
- export const addListenerByKey = (key: string, listener: () => void): void => {
13
- if (typeof window === 'undefined') {
14
- return;
15
- }
16
-
17
- if (getListenersKeysSize() === 0) {
18
- addEventListener('storage', listenExternalStoreEvents);
19
- }
20
-
21
- if(!isDefined(window.__AKB2_LOCAL_STORAGE__)){
22
- window.__AKB2_LOCAL_STORAGE__ = { } as typeof window.__AKB2_LOCAL_STORAGE__;
23
- }
24
-
25
- if(!isDefined(window.__AKB2_LOCAL_STORAGE__.listeners)){
26
- window.__AKB2_LOCAL_STORAGE__.listeners = new Map();
27
- }
28
-
29
- if (!window.__AKB2_LOCAL_STORAGE__.listeners.has(key)) {
30
- window.__AKB2_LOCAL_STORAGE__.listeners.set(key, new Set());
31
- }
32
-
33
- window.__AKB2_LOCAL_STORAGE__.listeners.get(key)!.add(listener);
34
- };
@@ -1,25 +0,0 @@
1
- import { isDefined, Nullable } from "@akb2/types-tools";
2
-
3
- export const clearLocalStorage = (): void => {
4
- if (typeof window === 'undefined') {
5
- return;
6
- }
7
-
8
- const keys = Array.from(window.__AKB2_LOCAL_STORAGE__?.listeners?.keys()??[]);
9
- const currentValues = keys.reduce((acc, key) => {
10
- acc.set(key, localStorage.getItem(key) ?? null);
11
-
12
- return acc;
13
- }, new Map<string, Nullable<string>>());
14
-
15
- localStorage.clear();
16
- window.__AKB2_LOCAL_STORAGE__?.originalData?.clear();
17
-
18
- window.__AKB2_LOCAL_STORAGE__?.listeners?.forEach((listeners, key) => {
19
- const currentValue = currentValues.get(key);
20
-
21
- if (isDefined(currentValue)) {
22
- listeners.forEach((listener) => listener());
23
- }
24
- });
25
- };
@@ -1,18 +0,0 @@
1
- import { isDefined } from "@akb2/types-tools";
2
-
3
- /**
4
- * Deeply freezes an object, making it immutable.
5
- *
6
- * @param value - The object to freeze.
7
- * @returns The frozen object.
8
- */
9
- export const deepFreeze = <T>(value: T): T => {
10
- if (!isDefined(value) || typeof value !== 'object' || Object.isFrozen(value)) {
11
- return value;
12
- }
13
-
14
- Object.freeze(value);
15
- Object.values(value as Record<string, unknown>).forEach(deepFreeze);
16
-
17
- return value;
18
- };
@@ -1,29 +0,0 @@
1
- import { getListenersKeysSize } from "@utils/get-listeners-keys-size";
2
- import { listenExternalStoreEvents } from "@utils/listen-external-store-events";
3
-
4
- /**
5
- * Deletes a listener for changes to a specific key in local storage.
6
- *
7
- * @param key The key in local storage to stop listening for changes on.
8
- * @param listener The callback function to remove.
9
- * @returns void
10
- */
11
- export const deleteListenerByKey = (key: string, listener: () => void): void => {
12
- if (typeof window === 'undefined') {
13
- return;
14
- }
15
-
16
- if (!window.__AKB2_LOCAL_STORAGE__?.listeners?.has(key)) {
17
- return;
18
- }
19
-
20
- window.__AKB2_LOCAL_STORAGE__.listeners.get(key)!.delete(listener);
21
-
22
- if (window.__AKB2_LOCAL_STORAGE__.listeners.get(key)?.size === 0) {
23
- window.__AKB2_LOCAL_STORAGE__.listeners.delete(key);
24
- }
25
-
26
- if (getListenersKeysSize() === 0) {
27
- removeEventListener('storage', listenExternalStoreEvents);
28
- }
29
- };
@@ -1,14 +0,0 @@
1
- import { anyToInt } from "@akb2/types-tools";
2
-
3
- /**
4
- * Gets the number of keys that have listeners in local storage.
5
- *
6
- * @returns The number of keys with listeners.
7
- */
8
- export const getListenersKeysSize = (): number => {
9
- if (typeof window === 'undefined') {
10
- return 0;
11
- }
12
-
13
- return anyToInt(window.__AKB2_LOCAL_STORAGE__?.listeners?.size);
14
- }
@@ -1,49 +0,0 @@
1
- import { isDefined, NotDefinable } from "@akb2/types-tools";
2
- import { deepFreeze } from "@utils/deep-freeze";
3
- import { getOriginalDataStorageKey } from "./get-original-data-storage-key";
4
-
5
- /**
6
- * Retrieves the value associated with a specific key from local storage.
7
- *
8
- * @template T The expected type of the value.
9
- * @param key The key in local storage to retrieve the value for.
10
- * @returns The value associated with the key, or undefined if not found.
11
- */
12
- export const getLocalStorageValue = <T>(key: string): NotDefinable<T> => {
13
- if (typeof window === 'undefined') {
14
- return undefined;
15
- }
16
-
17
- const raw = localStorage.getItem(key);
18
-
19
- if (!isDefined(raw)) {
20
- return undefined;
21
- }
22
-
23
- if(!isDefined(window.__AKB2_LOCAL_STORAGE__)){
24
- window.__AKB2_LOCAL_STORAGE__ = { } as typeof window.__AKB2_LOCAL_STORAGE__;
25
- }
26
-
27
- if(!isDefined(window.__AKB2_LOCAL_STORAGE__.originalData)){
28
- window.__AKB2_LOCAL_STORAGE__.originalData = new Map();
29
- }
30
-
31
- try {
32
- const cachedKey = getOriginalDataStorageKey(key, raw);
33
- const cachedData = window.__AKB2_LOCAL_STORAGE__.originalData.get(cachedKey);
34
-
35
- if (isDefined(cachedData)) {
36
- return cachedData as T;
37
- }
38
-
39
- const parsedData = deepFreeze(JSON.parse(raw).value);
40
-
41
- if (isDefined(parsedData)) {
42
- window.__AKB2_LOCAL_STORAGE__.originalData.set(cachedKey, parsedData);
43
- }
44
-
45
- return parsedData as Readonly<T>;
46
- } catch {
47
- return raw.length > 0 ? (raw as unknown as T) : undefined;
48
- }
49
- };
@@ -1,8 +0,0 @@
1
- /**
2
- * Generates a unique key for storing the original data in local storage.
3
- *
4
- * @param key The key in local storage.
5
- * @param value The value associated with the key.
6
- * @returns A unique key for storing the original data.
7
- */
8
- export const getOriginalDataStorageKey = (key: string, value: string): string => `${key}:{${value}}`;
@@ -1,35 +0,0 @@
1
- import { isDefined } from "@akb2/types-tools";
2
- import { getOriginalDataStorageKey } from "@utils/get-original-data-storage-key";
3
-
4
- /**
5
- * Listens for external changes to the local storage and updates the internal cache and listeners accordingly.
6
- *
7
- * @param event The storage event triggered by changes to the local storage.
8
- * @returns void
9
- */
10
- export const listenExternalStoreEvents = (event: StorageEvent): void => {
11
- if (event.storageArea === localStorage) {
12
- if(!isDefined(window.__AKB2_LOCAL_STORAGE__)){
13
- window.__AKB2_LOCAL_STORAGE__ = { } as typeof window.__AKB2_LOCAL_STORAGE__;
14
- }
15
-
16
- if(!isDefined(window.__AKB2_LOCAL_STORAGE__.listeners)){
17
- window.__AKB2_LOCAL_STORAGE__.listeners = new Map();
18
- }
19
-
20
- if(!isDefined(window.__AKB2_LOCAL_STORAGE__.originalData)){
21
- window.__AKB2_LOCAL_STORAGE__.originalData = new Map();
22
- }
23
-
24
- if (isDefined(event.key)) {
25
- if (isDefined(event.oldValue) && event.oldValue !== event.newValue) {
26
- window.__AKB2_LOCAL_STORAGE__.originalData.delete(getOriginalDataStorageKey(event.key, event.oldValue));
27
- }
28
-
29
- window.__AKB2_LOCAL_STORAGE__.listeners.get(event.key)?.forEach((listener) => listener());
30
- } else {
31
- window.__AKB2_LOCAL_STORAGE__.originalData.clear();
32
- window.__AKB2_LOCAL_STORAGE__.listeners.forEach((listeners) => listeners.forEach((listener) => listener()));
33
- }
34
- }
35
- };
@@ -1,3 +0,0 @@
1
- import { setLocalStorageValue } from "@utils/set-local-storage-value";
2
-
3
- export const removeLocalStorageValue = (key: string): void => setLocalStorageValue(key, null);
@@ -1,50 +0,0 @@
1
- import { isDefined, Nullable } from "@akb2/types-tools";
2
- import { deepFreeze } from "@utils/deep-freeze";
3
- import { getLocalStorageValue } from "@utils/get-local-storage-value";
4
- import { getOriginalDataStorageKey } from "@utils/get-original-data-storage-key";
5
- import { SetStateAction } from "react";
6
-
7
- export const setLocalStorageValue = <T>(key: string, valueOrCallback: SetStateAction<Nullable<T>>): void => {
8
- if (typeof window === 'undefined') {
9
- return;
10
- }
11
-
12
- const value = typeof valueOrCallback === 'function' ? (valueOrCallback as Function)(getLocalStorageValue<T>(key)) : valueOrCallback;
13
-
14
- if (typeof value === 'function') {
15
- throw new Error('Functional updates are not supported in useLocalStorageState setter. Please provide the new value directly.');
16
- }
17
-
18
- const currentValue = localStorage.getItem(key) ?? null;
19
- const newValue = isDefined(value) ? JSON.stringify({ value }) : null;
20
-
21
- if (isDefined(newValue)) {
22
- localStorage.setItem(key, newValue);
23
- } else {
24
- localStorage.removeItem(key);
25
- }
26
-
27
- if (currentValue !== newValue) {
28
- if(!isDefined(window.__AKB2_LOCAL_STORAGE__)){
29
- window.__AKB2_LOCAL_STORAGE__ = { } as typeof window.__AKB2_LOCAL_STORAGE__;
30
- }
31
-
32
- if(!isDefined(window.__AKB2_LOCAL_STORAGE__.listeners)){
33
- window.__AKB2_LOCAL_STORAGE__.listeners = new Map();
34
- }
35
-
36
- if(!isDefined(window.__AKB2_LOCAL_STORAGE__.originalData)){
37
- window.__AKB2_LOCAL_STORAGE__.originalData = new Map();
38
- }
39
-
40
- window.__AKB2_LOCAL_STORAGE__.listeners.get(key)?.forEach((listener) => listener());
41
-
42
- if (isDefined(currentValue)) {
43
- window.__AKB2_LOCAL_STORAGE__.originalData.delete(getOriginalDataStorageKey(key, currentValue));
44
- }
45
-
46
- if (isDefined(newValue)) {
47
- window.__AKB2_LOCAL_STORAGE__.originalData.set(getOriginalDataStorageKey(key, newValue), deepFreeze(JSON.parse(newValue).value));
48
- }
49
- }
50
- };
@@ -1,28 +0,0 @@
1
- import { clearLocalStorage, useLocalStorageState } from "@source";
2
- import { cleanup, renderHook, waitFor } from "@testing-library/react";
3
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4
-
5
- beforeEach(() => localStorage.clear());
6
-
7
- afterEach(() => {
8
- cleanup();
9
- clearLocalStorage();
10
- vi.useRealTimers();
11
- });
12
-
13
- describe("LocalStorage direct changing", () => {
14
- it("No support changes with direct localStorage modification", () => {
15
- const { result } = renderHook(() => useLocalStorageState("initial"));
16
-
17
- expect(result.current[0]).toBeUndefined();
18
-
19
- localStorage.setItem(
20
- "initial",
21
- JSON.stringify({ value: "new value with direct modification" }),
22
- );
23
-
24
- return waitFor(() => expect(result.current[0]).toBe(undefined), {
25
- timeout: 2000,
26
- });
27
- });
28
- });
@@ -1,40 +0,0 @@
1
- import { clearLocalStorage, useLocalStorageState } from "@source";
2
- import { act, cleanup, renderHook } from "@testing-library/react";
3
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4
-
5
- beforeEach(() => localStorage.clear());
6
-
7
- afterEach(() => {
8
- cleanup();
9
- clearLocalStorage();
10
- vi.useRealTimers();
11
- });
12
-
13
- describe("A simple value changing", () => {
14
- it("No changing a value", () => {
15
- const { result } = renderHook(() => useLocalStorageState("initial"));
16
-
17
- expect(result.current[0]).toBeUndefined();
18
- });
19
-
20
- it("Changing a value", () => {
21
- const { result } = renderHook(() => useLocalStorageState("initial"));
22
-
23
- expect(result.current[0]).toBeUndefined();
24
- act(() => result.current[1]("new value"));
25
- expect(result.current[0]).toBe("new value");
26
- });
27
-
28
- it("Change a value with timeout", () => {
29
- vi.useFakeTimers();
30
-
31
- const { result } = renderHook(() => useLocalStorageState("initial"));
32
-
33
- expect(result.current[0]).toBeUndefined();
34
- setTimeout(() => result.current[1]("new value with timeout"), 100);
35
- act(() => vi.advanceTimersByTime(99));
36
- expect(result.current[0]).toBeUndefined();
37
- act(() => vi.advanceTimersByTime(1));
38
- expect(result.current[0]).toBe("new value with timeout");
39
- });
40
- });
@@ -1,3 +0,0 @@
1
- {
2
- "extends": "../tsconfig.test.json"
3
- }
package/tsconfig.json DELETED
@@ -1,33 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "module": "ESNext",
5
- "declaration": true,
6
- "strict": true,
7
- "moduleResolution": "Bundler",
8
- "skipLibCheck": true,
9
- "outDir": ".dist",
10
- "rootDir": ".",
11
- "esModuleInterop": true,
12
- "jsx": "react-jsx",
13
- "jsxImportSource": "preact",
14
- "types": [
15
- "react",
16
- "react-dom"
17
- ],
18
- "paths": {
19
- "@hooks/*": [
20
- "./src/hooks/*"
21
- ],
22
- "@utils/*": [
23
- "./src/utils/*"
24
- ],
25
- "@types/*": [
26
- "./src/types/*"
27
- ]
28
- }
29
- },
30
- "include": [
31
- "src"
32
- ]
33
- }
@@ -1,31 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "noEmit": true,
5
- "declaration": false,
6
- "rootDir": ".",
7
- "lib": [
8
- "ES2020",
9
- "DOM",
10
- "DOM.Iterable"
11
- ],
12
- "types": [
13
- "node",
14
- "vitest/globals"
15
- ],
16
- "paths": {
17
- "@source": [
18
- "./src/index.ts"
19
- ]
20
- }
21
- },
22
- "include": [
23
- "tests/**/*.ts",
24
- "tests/**/*.tsx",
25
- "vitest.config.ts"
26
- ],
27
- "exclude": [
28
- "node_modules",
29
- ".dist"
30
- ]
31
- }
package/vitest.config.ts DELETED
@@ -1,19 +0,0 @@
1
- import { fileURLToPath } from "node:url";
2
- import { defineConfig } from "vitest/config";
3
-
4
- export default defineConfig({
5
- test: {
6
- environment: "jsdom",
7
- environmentOptions: {
8
- jsdom: {
9
- url: "http://localhost/",
10
- },
11
- },
12
- alias: {
13
- "@source": fileURLToPath(new URL("./src/index.ts", import.meta.url)),
14
- "@utils": fileURLToPath(new URL("./src/utils", import.meta.url)),
15
- "@types": fileURLToPath(new URL("./src/types", import.meta.url)),
16
- "@hooks": fileURLToPath(new URL("./src/hooks", import.meta.url)),
17
- },
18
- },
19
- });