react-state-monad 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
package/README.md ADDED
@@ -0,0 +1,248 @@
1
+ # React State Monad
2
+
3
+ A set of hooks to manage/transform/filter states with monads in React.
4
+
5
+ ## Description
6
+
7
+ `react-state-monad` provides a set of monadic state management utilities, specifically designed to work seamlessly with
8
+ React's state hooks. It allows you to manage, transform, and filter states in a functional and declarative way using
9
+ monads like `Maybe` and `Option`.
10
+
11
+ This library leverages the power of monads to encapsulate state changes, enabling a cleaner, more predictable way to
12
+ handle various state conditions in React.
13
+
14
+ ## Features
15
+
16
+ - Manage state using monads like `Maybe` and `Option`.
17
+ - Simplify handling of undefined or null values in state.
18
+ - Leverage functional programming patterns in React state management.
19
+ - Support for TypeScript with full type definitions.
20
+
21
+ ## Installation
22
+
23
+ You can install `react-state-monad` using npm or yarn:
24
+
25
+ ```bash
26
+ npm install react-state-monad
27
+ ```
28
+
29
+ or
30
+
31
+ ```bash
32
+ yarn add react-state-monad
33
+ ```
34
+
35
+ ## Usage
36
+
37
+ Here's an example of how you can use the hooks in your React components:
38
+
39
+ ### `useStateObject<T>`
40
+
41
+ This hook initializes a StateObject with the provided initial value. It uses React's useState hook to manage the
42
+ internal state and returns a StateObject that represents the current state.
43
+
44
+ Parameters: `initialState`: The initial value of the state. This value can be of any type, determined by the generic
45
+ type `T`.
46
+
47
+ Returns a `StateObject` of type `T` representing the initialized state. This `StateObject` is an instance
48
+ of `ValidState`, which
49
+ encapsulates the state value and provides a `setValue` function to update it.
50
+
51
+ ### `useEmptyState<T>`
52
+
53
+ This hook initializes a `StateObject` with an empty state. It is useful as a fallback when no valid state is available.
54
+
55
+ Parameters: None.
56
+
57
+ Returns a `StateObject` of type `T` representing an empty state. This `StateObject` is an instance of `EmptyState`,
58
+ which encapsulates the absence of a state value.
59
+
60
+ ### `useElementState<T>`
61
+
62
+ This hook allows you to derive and update a specific element in an array within a `StateObject`. It is useful for
63
+ managing state at a granular level within an array.
64
+
65
+ Parameters:
66
+
67
+ - `state`: The `StateObject` containing an array.
68
+ - `index`: The index of the element to be derived.
69
+
70
+ Returns a `StateObject` of type `T` representing the element at the given index. If the index is out of bounds or the
71
+ state is empty, it returns an instance of `EmptyState`. Otherwise, it returns an instance of `ValidState`, which
72
+ encapsulates the element value and provides a `setValue` function to update it.
73
+
74
+ ### `useFieldState<TOriginal, TField>`
75
+
76
+ This hook derives a field from the state object and creates a new `StateObject` for the field's value. It is useful for
77
+ managing state at a granular level within an object.
78
+
79
+ Parameters:
80
+
81
+ - `state`: The `StateObject` containing the original state.
82
+ - `field`: The field name to be derived from the state.
83
+
84
+ Returns a `StateObject` of type `TField` representing the derived field. This `StateObject` is an instance
85
+ of `ValidState`, which encapsulates the field value and provides a `setValue` function to update it.
86
+
87
+ For example:
88
+
89
+ ```jsx
90
+ import React from 'react';
91
+ import {useStateObject, useFieldState} from 'react-state-monad';
92
+
93
+ const MyComponent = () => {
94
+ const userState = useStateObject({
95
+ name: 'John Doe',
96
+ age: 30,
97
+ });
98
+
99
+ const nameState = useFieldState(userState, 'name');
100
+ const ageState = useFieldState(userState, 'age');
101
+
102
+ return (
103
+ <div>
104
+ <input
105
+ type="text"
106
+ value={nameState.value}
107
+ onChange={(e) => nameState.value = e.target.value}
108
+ />
109
+ <input
110
+ type="number"
111
+ value={ageState.value}
112
+ onChange={(e) => ageState.value = parseInt(e.target.value, 10)}
113
+ />
114
+ </div>
115
+ );
116
+ };
117
+
118
+ export default MyComponent;
119
+ ```
120
+
121
+ ### `useRemapArray<T>`
122
+
123
+ This hook maps each element in an array within a `StateObject` to a new `StateObject`, allowing for independent updates
124
+ of each element while keeping the overall array state synchronized.
125
+
126
+ Parameters:
127
+
128
+ - `state`: The `StateObject` containing an array.
129
+
130
+ Returns an array of new `StateObject`s, each representing an element in the original array. This allows individual
131
+ updates while keeping the array state synchronized. If the state has no value, it returns an empty array.
132
+
133
+ ### Complete Example
134
+
135
+ Here's a more complete example demonstrating the usage of `useStateObject`, `useFieldState`, `useElementState`,
136
+ and `useRemapArray` hooks in a React component:
137
+
138
+ ```tsx
139
+
140
+ const AgeField = (props: { ageState: StateObject<number> }) => <div>
141
+ <label>Age:</label>
142
+ <input
143
+ type="number"
144
+ value={props.ageState.value}
145
+ onChange={x => props.ageState.value = parseInt(x.target.value, 10)}
146
+ />
147
+ </div>;
148
+
149
+
150
+ const NameField = (props: { nameState: StateObject<string> }) => {
151
+ return <div>
152
+ <label>Name:</label>
153
+ <input
154
+ type="text"
155
+ value={props.nameState.value}
156
+ onChange={x => props.nameState.value = x.target.value}
157
+ />
158
+ </div>;
159
+ }
160
+
161
+ const HobbyField = (props: { hobbyState: StateObject<string> }) => {
162
+ return <div>
163
+ <input
164
+ type="text"
165
+ value={props.hobbyState.value}
166
+ onChange={x => props.hobbyState.value = x.target.value}
167
+ />
168
+ </div>;
169
+ }
170
+
171
+ const HobbiesField = (props: { hobbiesState: StateObject<string[]> }) => {
172
+
173
+ const hobbyStates: StateObject<string>[] = useRemapArray(props.hobbiesState);
174
+
175
+ const addHobby = () => {
176
+ // Always use the setter to update arrays, do not modify them directly to ensure React state consistency.
177
+ // Immutability is key 💗
178
+ props.hobbiesState.value = [...props.hobbiesState.value, ''];
179
+ }
180
+
181
+ return <div>
182
+ <label>Hobbies:</label>
183
+ {
184
+ hobbyStates.map((hobbyState, index) => <HobbyField key={index} hobbyState={hobbyState}/>)
185
+ }
186
+ <button onClick={addHobby}>Add Hobby</button>
187
+ </div>;
188
+
189
+
190
+ };
191
+
192
+ export const UserProfile = () => {
193
+
194
+ type DudeData = {
195
+ name: string;
196
+ age: number;
197
+ hobbies: string[];
198
+ }
199
+ // Initialize state with an object containing user details and an array of hobbies
200
+ const userState: StateObject<DudeData> = useStateObject({
201
+ name: 'John Doe',
202
+ age: 30,
203
+ hobbies: ['Reading', 'Traveling', 'Cooking'],
204
+ });
205
+
206
+ // Derive state for individual fields
207
+ const nameState: StateObject<string> = useFieldState(userState, 'name');
208
+ const ageState: StateObject<number> = useFieldState(userState, 'age');
209
+
210
+ // Derive state for hobbies array
211
+ const hobbiesState: StateObject<string[]> = useFieldState(userState, 'hobbies');
212
+
213
+
214
+ return (
215
+ <div>
216
+ <h1>User Profile</h1>
217
+ <NameField nameState={nameState}/>
218
+ <AgeField ageState={ageState}/>
219
+ <HobbiesField hobbiesState={hobbiesState}/>
220
+ </div>
221
+ );
222
+ };
223
+ ```
224
+
225
+ ## Contributing
226
+
227
+ Contributions are welcome! If you'd like to contribute to this library, please fork the repository and submit a pull
228
+ request.
229
+
230
+ How to Contribute
231
+ Fork the repository.
232
+
233
+ * Create a new branch for your feature `git checkout -b feature-name`
234
+ * Commit your changes `git commit -am 'Add new feature'`
235
+ * Push to the branch `git push origin feature-name`
236
+ * Open a pull request. I'll be happy to review it!
237
+
238
+ ## License
239
+
240
+ This project is licensed under the GPL-3.0 License.
241
+
242
+ ## Author
243
+
244
+ `Marcos Alvarez`
245
+
246
+ [<img src="https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png" width="38" height="38">](https://github.com/alvmivan)
247
+ [<img src="https://www.linkedin.com/favicon.ico" width="40" height="40">](https://www.linkedin.com/in/marcos-alvarez-40651b150/)
248
+ [<img src="https://ssl.gstatic.com/ui/v1/icons/mail/rfr/gmail.ico" width="40" height="40">](mailto:alvmivan@gmail.com)
package/dist/index.cjs ADDED
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+
25
+ // src/index.tsx
26
+ var import_react2 = __toESM(require("react"), 1);
27
+
28
+ // src/hooks/useStateObject.ts
29
+ var import_react = require("react");
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ // src/index.tsx
2
+ import React from "react";
3
+
4
+ // src/hooks/useStateObject.ts
5
+ import { useState } from "react";
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "react-state-monad",
3
+ "type": "module",
4
+ "version": "0.0.1",
5
+ "description": "A set of hooks to manage/transform/filter states with monads in React",
6
+ "keywords": [
7
+ "maybe",
8
+ "option",
9
+ "monad",
10
+ "state",
11
+ "monads",
12
+ "react",
13
+ "functional"
14
+ ],
15
+ "scripts": {
16
+ "validateTypes": "tsc --noEmit",
17
+ "build": "tsup src/index.ts --format cjs,esm --dts",
18
+ "cleanBuild" : "rm -rf dist"
19
+ },
20
+ "dependencies": {
21
+ "react": "^19.0.0"
22
+ },
23
+ "devDependencies": {
24
+ "@types/react": "^19.0.7",
25
+ "tsup": "^8.3.5",
26
+ "typescript": "^5.7.3"
27
+ },
28
+ "author": {
29
+ "name": "Marcos Alvarez",
30
+ "email": "alvmivan@gmail.com",
31
+ "url": "https://github.com/alvmivan"
32
+ },
33
+ "license": "GPL-3.0",
34
+ "main": "dist/index.js",
35
+ "module": "dist/index.mjs",
36
+ "types": "dist/types/index.d.ts"
37
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Helper type that ensures a field is a valid key of an object and that the field's type matches the expected type.
3
+ *
4
+ * @template TObject - The object type.
5
+ * @template TField - The expected type of the field.
6
+ */
7
+ export type ValidFieldFrom<TObject, TField> = {
8
+ [Key in keyof TObject]: TObject[Key] extends TField ? Key : never;
9
+ }[keyof TObject];
10
+
11
+
12
+
@@ -0,0 +1,26 @@
1
+ import {StateObject} from "../stateObject";
2
+ import {EmptyState} from "../implementations/emptyState";
3
+ import {ValidState} from "../implementations/validState";
4
+
5
+ /**
6
+ * Hook that allows you to derive and update a specific element in an array within a StateObject.
7
+ *
8
+ * @template T - The type of the array elements.
9
+ * @param state - The StateObject containing an array.
10
+ * @param index - The index of the element to be derived.
11
+ * @returns A new StateObject representing the element at the given index.
12
+ */
13
+ export function useElementState<T>(state: StateObject<T[]>, index: number): StateObject<T> {
14
+ if (!state.hasValue || index < 0 || index >= state.value.length) {
15
+ return new EmptyState<T>(); // Returns an empty state if the index is out of bounds or state is empty.
16
+ }
17
+
18
+ return new ValidState<T>(
19
+ state.value[index],
20
+ (newElement) => {
21
+ const arrayCopy = [...state.value];
22
+ arrayCopy[index] = newElement;
23
+ state.value = arrayCopy;
24
+ }
25
+ );
26
+ }
@@ -0,0 +1,13 @@
1
+ import {StateObject} from "../stateObject";
2
+ import {EmptyState} from "../implementations/emptyState";
3
+
4
+ /**
5
+ * Hook that initializes a StateObject with an empty state.
6
+ * This is useful as a fallback when no valid state is available.
7
+ *
8
+ * @template T - The type of the value that could be held by the state.
9
+ * @returns A StateObject representing an empty state.
10
+ */
11
+ export function useEmptyState<T>(): StateObject<T> {
12
+ return new EmptyState<T>(); // Returns a new EmptyState object.
13
+ }
@@ -0,0 +1,21 @@
1
+ import {StateObject} from "../stateObject";
2
+ import {ValidFieldFrom} from "./types";
3
+
4
+ /**
5
+ * Hook that derives a field from the state object and creates a new StateObject for the field's value.
6
+ *
7
+ * @template TOriginal - The type of the original state object.
8
+ * @template TField - The type of the field value to be derived.
9
+ * @param state - The StateObject containing the original state.
10
+ * @param field - The field name to be derived from the state.
11
+ * @returns A new StateObject for the derived field.
12
+ */
13
+ export function useFieldState<TOriginal, TField>(
14
+ state: StateObject<TOriginal>,
15
+ field: ValidFieldFrom<TOriginal, TField>
16
+ ): StateObject<TField> {
17
+ return state.map(
18
+ (original) => original[field] as TField, // Extracts the field value.
19
+ (newField, original) => ({...original, [field]: newField} as TOriginal) // Updates the field with the new value.
20
+ );
21
+ }
@@ -0,0 +1,33 @@
1
+ import {StateObject} from "../stateObject";
2
+ import {ValidState} from "../implementations/validState";
3
+
4
+ /**
5
+ * Hook that maps each element in an array within a StateObject to a new StateObject,
6
+ * allowing for independent updates of each element while keeping the overall array state synchronized.
7
+ *
8
+ * @template T - The type of the array elements.
9
+ * @param state - The StateObject containing an array.
10
+ * @returns An array of new StateObjects, each representing an element in the original array,
11
+ * allowing individual updates while keeping the array state synchronized.
12
+ */
13
+ export function useRemapArray<T>(state: StateObject<T[]>): StateObject<T>[] {
14
+ if (!state.hasValue) return [] // Returns an empty array if the state has no value.
15
+
16
+ const count = state.value.length
17
+ const result: StateObject<T>[] = []
18
+
19
+ for (let i = 0; i < count; i++) {
20
+ result.push(
21
+ new ValidState<T>(
22
+ state.value[i], // The current value of the element at index i.
23
+ (newElement) => { // Setter to update the element at index i in the array.
24
+ const arrayCopy = [...state.value]; // Create a copy of the original array.
25
+ arrayCopy[i] = newElement; // Replace the element at index i with the new element.
26
+ state.value = arrayCopy; // Update the state with the new array, triggering a re-render.
27
+ }
28
+ )
29
+ )
30
+ }
31
+
32
+ return result // Return the array of StateObjects representing each element.
33
+ }
@@ -0,0 +1,15 @@
1
+ import {StateObject} from "../stateObject";
2
+ import {useState} from "react";
3
+ import {ValidState} from "../implementations/validState";
4
+
5
+ /**
6
+ * Hook that initializes a StateObject with the given initial value.
7
+ *
8
+ * @template T - The type of the value to be stored in the state.
9
+ * @param initialState - The initial value of the state.
10
+ * @returns A StateObject representing the initialized state.
11
+ */
12
+ export function useStateObject<T>(initialState: T): StateObject<T> {
13
+ const [state, setState] = useState<T>(initialState);
14
+ return new ValidState<T>(state, setState); // Returns a new ValidState object with the initial state value.
15
+ }
@@ -0,0 +1,43 @@
1
+ import {StateObject} from "../stateObject";
2
+
3
+ /**
4
+ * Represents a state that holds no value and is considered "empty".
5
+ * This is used as a fallback or default when there is no valid state.
6
+ * you should NEVER use this class directly, use the `StateObject` interface instead.
7
+ * and create instances by using the hooks
8
+ * @template T - The type of the value that could be held by the state.
9
+ */
10
+ export class EmptyState<T> implements StateObject<T> {
11
+ // No value stored, returns an error when accessed.
12
+ get value(): T {
13
+ throw new Error("Not implemented");
14
+ }
15
+
16
+ get hasValue(): boolean {
17
+ return false;
18
+ }
19
+
20
+ orElse(orElse: T): T {
21
+ return orElse; // Returns the fallback value when the state is empty.
22
+ }
23
+
24
+ do() {
25
+ // No operation for empty state.
26
+ }
27
+
28
+ filter(): StateObject<T> {
29
+ return this; // The empty state remains unchanged when filtered.
30
+ }
31
+
32
+ set value(_: T) {
33
+ // No operation for setting a value in the empty state.
34
+ }
35
+
36
+ flatMap<U>(): StateObject<U> {
37
+ return new EmptyState<U>(); // Returns an empty state when flatMapped.
38
+ }
39
+
40
+ map<U>(): StateObject<U> {
41
+ return new EmptyState<U>(); // Returns an empty state when mapped.
42
+ }
43
+ }
@@ -0,0 +1,60 @@
1
+ import {EmptyState} from "./emptyState";
2
+ import {StateObject} from "../stateObject";
3
+
4
+ /**
5
+ * Represents a state that holds a valid value of type T.
6
+ * you should NEVER use this class directly, use the `StateObject` interface instead.
7
+ * and create instances by using the hooks
8
+ * @template T - The type of the value stored in the state.
9
+ */
10
+ export class ValidState<T> implements StateObject<T> {
11
+ private readonly state: T;
12
+ private readonly setter: (state: T) => void;
13
+
14
+ constructor(state: T, setter: (state: T) => void) {
15
+ this.state = state;
16
+ this.setter = setter;
17
+ }
18
+
19
+ get value(): T {
20
+ return this.state;
21
+ }
22
+
23
+ do(action: (t: T) => void) {
24
+ action(this.state); // Performs the given action on the state value.
25
+ }
26
+
27
+ orElse() {
28
+ return this.state; // Returns the state value as it is valid.
29
+ }
30
+
31
+ set value(newState: T) {
32
+ this.setter(newState); // Sets a new value for the state.
33
+ }
34
+
35
+ map<U>(
36
+ mappingFunction: (t: T) => U,
37
+ inverseMappingFunction: (u: U, t: T) => T
38
+ ): StateObject<U> {
39
+ const derivedState = mappingFunction(this.state);
40
+ const derivedSetter = (newState: U) => {
41
+ this.setter(inverseMappingFunction(newState, this.state)); // Updates the state with the inverse mapping.
42
+ };
43
+
44
+ return new ValidState<U>(derivedState, derivedSetter); // Returns a new state object with the transformed value.
45
+ }
46
+
47
+ flatMap<U>(
48
+ mappingFunction: (t: T) => StateObject<U>
49
+ ): StateObject<U> {
50
+ return mappingFunction(this.state); // Applies the mapping function and returns the result.
51
+ }
52
+
53
+ get hasValue(): boolean {
54
+ return true;
55
+ }
56
+
57
+ filter(predicate: (t: T) => boolean): StateObject<T> {
58
+ return predicate(this.state) ? (this as StateObject<T>) : new EmptyState<T>(); // Filters the state based on the predicate.
59
+ }
60
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ import {StateObject} from "./stateObject";
2
+
3
+ // an empty index just to make tsc happy
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Represents a state object that holds a value of type T, allowing various state operations.
3
+ * This is the main interface for managing state, with operations like `map`, `filter`, and `flatMap`.
4
+ * initialize with useStateObject<T>(initialState: T) hook
5
+ * @template T - The type of the value stored in the state object.
6
+ */
7
+ export type StateObject<T> = {
8
+ /**
9
+ * The current value of the state.
10
+ */
11
+ get value(): T;
12
+
13
+ /**
14
+ * Returns true if the state has a valid value, false otherwise.
15
+ */
16
+ get hasValue(): boolean;
17
+
18
+ /**
19
+ * Performs an action on the current state value.
20
+ *
21
+ * @param action - A function that accepts the current state value and performs some operation.
22
+ */
23
+ do(action: (t: T) => void): void;
24
+
25
+ /**
26
+ * Sets a new value for the state.
27
+ *
28
+ * @param newState - The new state value to set.
29
+ */
30
+ set value(newState: T);
31
+
32
+ /**
33
+ * Transforms the current state into another state object by applying a mapping function.
34
+ *
35
+ * @template U - The type of the new state value.
36
+ * @param mappingFunction - A function that transforms the current state value into a new value.
37
+ * @param inverseMappingFunction - A function that transforms a new value back to the original state type.
38
+ * @returns A new StateObject with the transformed value.
39
+ */
40
+ map<U>(
41
+ mappingFunction: (t: T) => U,
42
+ inverseMappingFunction: (u: U, t: T) => T
43
+ ): StateObject<U>;
44
+
45
+ /**
46
+ * Filters the state based on a predicate, returning an empty state if the predicate is not satisfied.
47
+ *
48
+ * @param predicate - A function that tests the current state value.
49
+ * @returns A new StateObject with the original value or an empty state.
50
+ */
51
+ filter(predicate: (t: T) => boolean): StateObject<T>;
52
+
53
+ /**
54
+ * Returns the current state value if it exists; otherwise, returns the provided alternative value.
55
+ *
56
+ * @param orElse - The value to return if the state does not have a valid value.
57
+ * @returns The current state value or the provided fallback value.
58
+ */
59
+ orElse(orElse: T): T;
60
+
61
+ /**
62
+ * Transforms the current state into another state object by applying a mapping function that returns a new state.
63
+ *
64
+ * @template U - The type of the new state value.
65
+ * @param mappingFunction - A function that transforms the current state value into another state object.
66
+ * @returns A new StateObject based on the result of the mapping function.
67
+ */
68
+ flatMap<U>(
69
+ mappingFunction: (t: T) => StateObject<U>
70
+ ): StateObject<U>;
71
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "esnext", // Usar un target moderno compatible
4
+ "module": "esnext", // Usar módulos ES
5
+ "moduleResolution": "node", // Resolución de módulos tipo Node
6
+ "declaration": true, // Generar los archivos .d.ts
7
+ "declarationDir": "./dist/types", // El directorio de las declaraciones de tipo
8
+ "outDir": "./dist", // El directorio de salida para el bundle
9
+ "strict": true, // Habilitar el modo estricto
10
+ "esModuleInterop": true, // Interoperabilidad con módulos ES
11
+ "skipLibCheck": true, // Omitir la comprobación de bibliotecas
12
+ "forceConsistentCasingInFileNames": true // Forzar la consistencia de las mayúsculas/minúsculas en los nombres de archivo
13
+ },
14
+ "include": [
15
+ "src/**/*" // Incluir los archivos fuente de la carpeta src
16
+ ],
17
+ "exclude": [
18
+ "node_modules" // Excluir node_modules
19
+ ]
20
+ }